![]() |
![]() |
Category: iterators | Component type: overview |
The basic idea of the iterator tag functions is quite simple: iterators have associated type information, and iterator tag functions provide access to that information. Specifically, they are used to determine an iterator's value type, distance type, and iterator category.
An iterator's category is the most specific concept that it is a model of: Input Iterator, Output Iterator, Forward Iterator, Bidirectional Iterator, or Random Access Iterator. This information is expressed in the C++ type system by defining five category tag types, input_iterator_tag, output_iterator_tag, forward_iterator_tag, bidirectional_iterator_tag, and random_access_iterator_tag, each of which corresponds to one of those concepts. [2] The function iterator_category takes a single argument, an iterator, and returns the tag corresponding to that iterator's category. That is, it returns a random_access_iterator_tag if its argument is a pointer, a bidirectional_iterator_tag if its argument is a list::iterator, and so on.
An iterator's value type is the type of object that is returned when the iterator is dereferenced. (See the discussion in the Input Iterator requirements.) Ideally, one might want value_type to take a single argument, an iterator, and return the iterator's value type. Unfortunately, however, that's impossible: a function must return an object, and types aren't objects. Instead, value_type returns the value (T*) 0, where T is the argument's value type. Note that the function value_type need not be defined for Output Iterators, since an Output Iterator need not have a value type.
An iterator's distance type is the type that is used to represent the distance between two iterators. (See the discussion in the Input Iterator requirements.) The function distance_type returns this information in the same form that value_type does: its argument is an iterator, and it returns the value (Distance*) 0, where Distance is the iterator's distance type. [3] Just as with value_type, the function distance_type need not be defined for Output Iterators: an Output Iterator need not have a distance type.
The functions iterator_category, value_type, and distance_type must be provided for every type of iterator. (Except, as noted above, that value_type and distance_type need not be provided for Output Iterators.) In principle, this is simply a matter of overloading: anyone who defines a new iterator type must define those three functions for it. In practice, there's a slightly more convenient method. The STL defines five base classes, output_iterator, input_iterator, forward_iterator, bidirectional_iterator, and random_access_iterator. The functions iterator_category, value_type, and distance_type are defined for those base classes. The effect, then, is that if you are defining a new type of iterator you can simply derive it from one of those base classes, and the iterator tag functions will automatically be defined correctly. These base classes contain no member functions or member variables, so deriving from one of them ought not to incur any overhead.
(Again, note that base classes are provided solely for the convenience of people who define iterators. If you define a class Iter that is a new kind of Bidirectional Iterator, you do not have to derive it from the base class bidirectional_iterator. You do, however, have to make sure that iterator_category, value_type, and distance_type are defined correctly for arguments of type Iter, and deriving Iter from bidirectional_iterator is usually the most convenient way to do that.)
template <class ForwardIterator1, class ForwardIterator2, class ValueType> inline void __iter_swap(ForwardIterator1 a, ForwardIterator2 b, ValueType*) { T tmp = *a; *a = *b; *b = tmp; } template <class ForwardIterator1, class ForwardIterator2> inline void iter_swap(ForwardIterator1 a, ForwardIterator2 b) { __iter_swap(a, b, value_type(a)); }This example uses the iterator_category iterator tag function: reverse can be implemented for either Bidirectional Iterators or for Random Access Iterators, but the algorithm for Random Access Iterators is more efficient. Consequently, reverse is written to dispatch on the iterator category. This dispatch takes place at compile time, and should not incur any run-time penalty.
template <class BidirectionalIterator> void __reverse(BidirectionalIterator first, BidirectionalIterator last, bidirectional_iterator_tag) { while (true) if (first == last || first == --last) return; else iter_swap(first++, last); } template <class RandomAccessIterator> void __reverse(RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag) { while (first < last) iter_swap(first++, --last); } template <class BidirectionalIterator> inline void reverse(BidirectionalIterator first, BidirectionalIterator last) { __reverse(first, last, iterator_category(first)); }
[1] Output Iterators have neither a distance type nor a value type; in many ways, in fact, Output Iterators aren't really iterators. Output iterators do not have a value type, because it is impossible to obtain a value from an output iterator but only to write a value through it. They do not have a distance type, similarly, because it is impossible to find the distance from one output iterator to another. Finding a distance requires a comparison for equality, and output iterators do not support operator==.
[2] Note that Trivial Iterator does not appear in this list. The Trivial Iterator concept is introduced solely for conceptual clarity; the STL does not actually define any Trivial Iterator types, so there is no need for a Trivial Iterator tag. There is, in fact, a strong reason not to define one: the C++ type system does not provide any way to distinguish between a pointer that is being used as a trivial iterator (that is, a pointer to an object that isn't part of an array) and a pointer that is being used as a Random Access Iterator into an array.
[3] These iterator tag functions may seem clumsy: it would be much more convenient to require that every iterator type Iter provide nested types Iter::iterator_category, Iter::value_type, and Iter::distance_type. Unfortunately, this is impossible: pointers are iterators (they are an extremely important kind of iterator), and pointers can't be retroactively modified to have nested types. A second option would be to define a templatized traits class, so that these types could be referred to as iterator_traits<Iter>::iterator_category, iterator_traits<Iter>::value_type, and iterator_traits<Iter>::distance_type. This solution is possible in principle, and it is an improvement on iterator tag functions. However, it relies on a C++ feature known as partial specialization. Partial specialization is part of the C++ standard, but no compilers currently (as of September 1996) support it. Once compilers that support partial specialization become available, future versions of the STL will use iterator traits.