Effective STL Terms 27

zhaozj2021-02-11  219

Terms 27: Translate const_iterator into iterator with distance and advance

Terms 26 pointed out that some container member functions accept only Iterator as a parameter, not const_iterator. So, if you only have a const_iterator, and you want to insert a new element in the container location it points to? That is, how do you convert the const_iterator to iterator? Because as explained in terms 26, there is no implicit conversion between const_iterator to iterator, so you must be the protagonist of this action.

I know what you are thinking. You are thinking, "When you don't have a road, just raise your big hammer!" In the world of C , you mean only: mapping. This idea is very shameful. I really don't know where you are learning.

Let us face problems in front of you. See what happens when you map a const_iterator mapping to iTerator:

Typedef deve intdeque; // typedef, simplified code.

TYPEDEF INTDEQUE :: item;

TypedEf INTDEQUE :: Const_Iterator constitr;

Constiter Ci; // CI is const_iterator

...

ITer i (ci); // error! No from Const_Iterator

/ / The way to Iterator implicit conversion

ITer I (const_cast (ci)); // is still an error! Can't be from const_iterator

// Map to Iterator!

Here is just as an example, but the result of the Hash table container [1]-of the other container -LIST, SET, MultiSet, Map, MultiMap, and even clause 25. Using the map of the map perhaps when you can compile when the version of Vector or String, this is the very special situation we have to discuss immediately.

The code containing the map cannot be compiled with the reason for these containers, Iterator and Const_Iteerator are completely different classes. They are not more blood relationships than String and Complex . The const_cast mapping between two unrelated classes is ridiculous, so ReinterPret_cast, Static_cast, and even C style mappings can also result in the same result.

Oh, the code that cannot be compiled may be able to compile by the vector and String container. That is because most implementations will use real pointers as an iterator of those containers. For this implementation, Vector :: Iterator is Typedef, and Vector :: Const_Iterator is typefedef, string :: iterator is a typedef of CHAR *, while string :: const_iterator Is const char * TypeDef. In this implementation, use const_cast to map const_iterator into iterator. Of course, it can be compiled and there is no problem, because const_cast mapping between const_iterator and iTerator is finally interpreted as a mapping of CONST T * to T *. However, even in this implementation, REVERSE_ITERATOR and CONST_REVERSE_ITERATOR are also real classes, so you can still map const_reverse_iterator to Reverse_iterator with const_cast. Moreover, as explained in Terms 50, these implementations are usually only used in Release mode to use a pointer to represent Vector and String iterators [2]. All of these facts indicate that the Const iterator maps to iterators are pathologically, even if it is also for Vector and String, it is worthy of doubt. If you get a const_iterator and you can access the container it points to, there is a secure, portable method to get the Iterator it correspond, and is not inverting the conversion of the type system. Below is the essence of solving the idea, although it is amended before it compiles:

Typedef Deque intdeque; //

TYPEDEF INTDEQUE :: item;

TypedEf INTDEQUE :: Const_Iterator constitr;

INTDEQUE D;

CONSTITER CI;

... // Let CI point D

ITer I (D. Segin ()); // Initialization I is D.BEGIN ()

Advance (i, distance (i, ci)); // Adjust I, point to CI

// (But please pay attention to why

// The reason for modification before it compiles)

This method looks very simple, straightforward, it is also very shocked. To get the Iterator pointing to the same location to the same location, first point the item to the starting position of the container, and let it move to the position of the offset from the const_iterator to the starting position of the container! This task gets the help of two practical algorithms Advance and Distance, which are declared in . Distance returns a distance between Iterator points to the same container; Advance is used to move an Iterator to the specified distance. If I and CI points to the same container, the expression Advance (I, DISTANCE (I, CI)) moves I to the same location as CI.

If this code can be compiled, it can complete this conversion task. But it seems that things are not so smooth. Want to know why, let's take a look at the definition of distance:

Template

TypeName Iterator_Traits :: Difference_Type

Distance (InputITerator First, InputITerator Last); Don't be trapped by this function of 56 characters, and you don't have to pay for Difference_Type. Instead, the focus is concentrated in the type of parameter inputiterator:

Template

TypeName Iterator_Traits :: Difference_Type

Distance (InputITerator First, InputITerator Last);

When you encounter a distance call, your compiler needs to infer the type of InputItemrator based on the parameter type used. Let's take a look at what I said is less right. Distance call:

Advance (i, distance (i, ci)); // Adjust I, point to CI

There are two parameters to deliver to Distance, I and CI. The type of i is iTer, namely Deque :: Iterator's TypeDef. For the compiler, this indicates that the DISTANCE's InputIterator is Deque :: Iterator. However, CI is constiter, named the TypedEf for Deque :: const_iterator. This indicates that INPUTITERATOR is Deque :: const_iterator. InputItemrator cannot have two different types at the same time, so call the Distance failed. Generally, some lengthy error messages may not be explained that the compiler cannot get the type of inputiterator.

To call Distance, you need to exclude ambiguity. The easiest way is to explicitly specify the type of template parameters called by Distance, which avoids the compiler yourself to get their type:

Advance (I, Distance (i, ci));

We now know how to get the corresponding Iterator through Advance and Distance. But another actual problem we have always avoided is very valuable is: What is the efficiency of this skill? The answer is simple. Depends on what iterators you are converting. This is a constant time operation for random access iters (such as Vector, String, and Deque). For bidirectional iterators (that is, all other containers and some implementations including Hash containers [3] (see Terms 25), this is the operation of linear times.

Because it may cost the price of linear time to generate one and const_iterator equivalent Iterator, and because if it is not possible to access the container to which the Const_Iiterator belongs, it cannot be completed. From this perspective, maybe you need to re-examine you from const_iterator to generate the design of Iterator. In fact, considering the provision of terms 26, it is recommended that you try to use Iterator as much as possible when handling the container.

[1] Two most common HASH-based STL containers are implemented from Dinkumware and SGI. You can find an overview of a Dinkumware method from the Cuj column "Hash table" in P. J.Plauger in November 1998. Amade the unique SGI implementation method of SGI from the clause of Effective STL 25, but its interface describes the SGI STL website.

[2] This happens when using Stlport debug mode. You can learn from Stlport's website to Stlport and its debug mode.

转载请注明原文地址:https://www.9cbs.com/read-4758.html

New Post(0)