diff --git a/iterator_adaptors.htm b/iterator_adaptors.htm index 72b5bff..976870a 100644 --- a/iterator_adaptors.htm +++ b/iterator_adaptors.htm @@ -32,9 +32,25 @@
iterator_adaptor is declared like this:
template <class Base, class Policies, class Value = typename std::iterator_traits<Base>::value_type, class Reference = ...(see below), class Pointer = ...(see below), class Category = typename std::iterator_traits<Base>::iterator_category, - class Distance = typename std::iterator_traits<Base>::difference_type - > + class Distance = typename std::iterator_traits<Base>::difference_type> struct iterator_adaptor; -{ - typedef Distance difference_type; - typedef typename boost::remove_const<Value>::type value_type; - typedef Pointer pointer; - typedef Reference reference; - typedef Category iterator_category; - typedef Base base_type; - typedef Policies policies_type; - - iterator_adaptor(); - iterator_adaptor(const Base&, const Policies& = Policies()); - - base_type base() const; - - template <class B, class V, class R, class P> - iterator_adaptor ( - const iterator_adaptor<B,Policies,V,R,P,Category,Distance>&); - - reference operator*() const; - operator_arrow_result_type operator->() const; [2] - value_type operator[](difference_type n) const; - - iterator_adaptor& operator++(); - iterator_adaptor& operator++(int); - iterator_adaptor& operator--(); - iterator_adaptor& operator--(int); - - iterator_adaptor& operator+=(difference_type n); - iterator_adaptor& operator-=(difference_type n); - - iterator_adaptor& operator-(Distance x) const; -}; - -template <class B, class P, class V, class R, class Ptr, - class C, class D1, class D2> -iterator_adaptor<B,P,V,R,Ptr,C,D1> -operator+(iterator_adaptor<B,P,V,R,Ptr,C,D1>, D2); - -template <class B, class P, class V, class R, class Ptr, - class C, class D1, class D2> -iterator_adaptor<B,P,V,R,P,C,D1> -operator+(D2, iterator_adaptor<B,P,V,R,Ptr,C,D1> p); - -template <class B1, class B2, class P, class V1, class V2, - class R1, class R2, class P1, class P2, class C, class D> -Distance operator-(const iterator_adaptor<B1,P,V1,R1,P1,C,D>&, - const iterator_adaptor<B2,P,V2,R2,P2,C,D>&); - -template <class B1, class B2, class P, class V1, class V2, - class R1, class R2, class P1, class P2, class C, class D> -bool operator==(const iterator_adaptor<B1,P,V1,R1,P1,C,D>&, - const iterator_adaptor<B2,P,V2,R2,P2,C,D>&); - -// and similarly for operators !=, <, <=, >=, >-
It is often useful to automatically apply some function to the - value returned by dereferencing (operator*()) an - iterator. The transform - iterator makes it easy to create an iterator adaptor that does - just that. Here we will show how easy it is to implement the - transform iterator using the iterator_adaptor class.
+Although iterator_adaptor takes seven template parameters, + defaults have been carefully chosen to minimize the number of parameters you + must supply in most cases, especially if BaseType is an iterator. + +
Parameter + + | Description + + |
---|---|
BaseType + + | The type being wrapped. + + |
Policies + + | A policy class + that supplies core functionality to the resulting iterator. A + detailed description can be found below. + + |
Value + + | The value_type of the resulting iterator, unless const. If Value is
+ const X, a conforming compiler makes the
+ value_type non-const X[1]. + Default: + std::iterator_traits<BaseType>::value_type + + |
Pointer + + | The pointer type of the resulting iterator, and in
+ particular, the result type of operator->(). + Default: If Value was supplied, then Value*, + otherwise std::iterator_traits<BaseType>::pointer. + + |
Reference + + | The reference type of the resulting iterator, and in
+ particular, the result type of operator*(). + Default: If Value is supplied, Value& is + used. Otherwise + std::iterator_traits<BaseType>::reference is used. + + |
Category + + | The iterator_category type for the resulting iterator. + Default: + std::iterator_traits<BaseType>::iterator_category + + |
Distance + + | The difference_type for the resulting iterator. + Default: + std::iterator_traits<BaseType>::difference_type + |
The main task in using iterator_adaptor is creating an - appropriate Policies class (a general description of the - policy class technique can be found here). + appropriate Policies class. - The Policies class that you pass in will become the heart of - the iterator adaptor, supplying the core iterator operations that will - determine how your new adaptor class will behave. The core iterator - operations are: + The Policies class will become the functional heart of the iterator + adaptor, supplying the core iterator operations that will determine how your + new adaptor class will behave. The iterator_adaptor template + defines all of the operators required of a + + Random Access Iterator. + Your Policies class must implement three, four, or seven of the core + iterator operations below depending on the iterator categories you want it to support. -
dereference
- returns an element of the iterator's
- reference
type
- equal
- tests the iterator for equality
- increment
- increments the iterator
+ Operation | Effects | Required for Iterator Categories + | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
dereference | returns an element of the iterator's + reference type | Input/ Output/ Forward/ Bidirectional/ Random Access
- decrement - decrements bidirectional and random-access
- iterators
+ equal | tests the iterator for equality
- | less - imposes a strict weak ordering relation on
- random-access iterators
+ increment | increments the iterator
- | distance - measures the distance between random-access
- iterators
+ decrement | decrements the iterator | Bidirectional/ Random Access
- | advance - adds an integer offset to random-access
- iterators
-
- The Policies class must implement three, four, or seven of the
- core iterator operations depending on whether you wish the new iterator
- adaptor class to be a ForwardIterator,
- BidirectionalIterator,
- or RandomAccessIterator.
- The iterator_category type of the traits class you pass in must
- match the category of iterator that you want to create. The default policy
- class, default_iterator_policies, implements all 7 of the core
+ less | imposes a Strict Weak
+ Ordering relation on the iterator's reference type
+ | Random
+ Access
+
+ | distance | measures the distance between iterators
+
+ | advance | adds an integer offset to iterators
+
+ | |
+ + The library also supplies a "trivial" policy class, + default_iterator_policies, which implements all seven of the core operations in the usual way. If you wish to create an iterator adaptor that - only changes a few of the iterator's behaviors, then you can have your new - policy class inherit from default_iterator_policies to avoid + only changes a few of the base type's behaviors, then you can derive your new + policy class from default_iterator_policies to avoid retyping the usual behaviors. You should also look at - default_iterator_policies as the ``boiler-plate'' for your own - policy classes. The following is the definition of the - default_iterator_policies class: + default_iterator_policies as the ``boilerplate'' for your own + policy classes, defining functions with the same interface. This is the definition of + default_iterator_policies:
+struct default_iterator_policies { - // required for a ForwardIterator - template <class Reference, class Iterator> - Reference dereference(type<Reference>, const Iterator& x) const + template <class Reference, class BaseType> + Reference dereference(type<Reference>, const BaseType& x) const { return *x; } - template <class Iterator> - static void increment(Iterator& x) + template <class BaseType> + static void increment(BaseType& x) { ++x; } - template <class Iterator1, class Iterator2> - bool equal(Iterator1& x, Iterator2& y) const + template <class BaseType1, class BaseType2> + bool equal(BaseType1& x, BaseType2& y) const { return x == y; } - // required for a BidirectionalIterator - template <class Iterator> - static void decrement(Iterator& x) + template <class BaseType> + static void decrement(BaseType& x) { --x; } - // required for a RandomAccessIterator - template <class Iterator, class DifferenceType> - static void advance(Iterator& x, DifferenceType n) + template <class BaseType, class DifferenceType> + static void advance(BaseType& x, DifferenceType n) { x += n; } - template <class Difference, class Iterator1, class Iterator2> - Difference distance(type<Difference>, Iterator1& x, Iterator2& y) const + template <class Difference, class BaseType1, class BaseType2> + Difference distance(type<Difference>, BaseType1& x, BaseType2& y) const { return y - x; } - template <class Iterator1, class Iterator2> - bool less(Iterator1& x, Iterator2& y) const + template <class BaseType1, class BaseType2> + bool less(BaseType1& x, BaseType2& y) const { return x < y; } };
Template member functions are used throughout +default_iterator_policies so that it can be employed with a wide range +of iterators. If we had used concrete types above, we'd have tied the usefulness +of default_iterator_policies to a particular range of adapted +iterators. If you follow the same pattern with your Policies classes, +you may achieve the same sort of reusability. + +
+
+iterator_adaptor(const Base&, const Policies& = Policies())
+ +Construct an adapted iterator from a base object and a policies object. + |
+template <class B, class V, class R, class P> +iterator_adaptor(const iterator_adaptor<B,Policies,V,R,P,Category,Distance>&) + +This constructor allows for conversion from non-const to constant +adapted iterators. See below for more details. +Requires: B is convertible to Base. + |
+base_type base() const;
+ +Return a copy of the base object. + |
It is often useful to automatically apply some function to the + value returned by dereferencing an + iterator. The transform + iterator makes it easy to create an iterator adaptor which does + just that. Here we will show how easy it is to implement the + transform iterator using the iterator_adaptor template.
+ +We want to be able to adapt a range of iterators and functions, so the + policies class will have a template parameter for the function type and it + will have a data member of that type. We know that the function takes + one argument and that we'll need to be able to deduce the + result_type of the function so we can use it for the adapted + iterator's value_type. + + AdaptableUnaryFunction is the + + Concept that fulfills those requirements. + +
+ To implement a transform iterator we will only change one of the base iterator's behaviors, so the - transform_iterator_policies class will inherit the rest - from default_iterator_policies. In addition, we will need - a function object to apply, so the policies class will have a - template parameter for the function object and it will have a data - member of that type. The function will take one argument (the - value type of the base iterator) and we will need to know the - result_type of the function, so - - AdaptableUnaryFunction is the correct - - concept to choose for the function object type. Inside of - transform_iterator_policies we will implement the - dereference() member function. This member function will - dereference the base iterator (the second parameter of - dereference()) and apply the function object. The - type<Reference> class used below is there to convey - the reference type of the iterator, which is handy when writing - generic iterator adaptors such as this one. The following is the - complete code for the transform_iterator_policies class. + transform_iterator_policies class can inherit the rest + from default_iterator_policies. We will define the +dereference() member function, which is used implement +operator*() of the adapted iterator. The implementation will +dereference the base iterator and apply the function object. The +type<Reference> parameter is used to convey the appropriate return +type. The complete code for transform_iterator_policies is:
@@ -276,7 +355,9 @@ struct default_iterator_policies struct transform_iterator_policies : public default_iterator_policies { transform_iterator_policies() { } - transform_iterator_policies(const AdaptableUnaryFunction& f) : m_f(f) { } + + transform_iterator_policies(const AdaptableUnaryFunction& f) + : m_f(f) { } template <class Reference, class BaseIterator> Reference dereference(type<Reference>, const BaseIterator& i) const @@ -288,15 +369,20 @@ struct default_iterator_policies
- The next step is to use the iterator_adaptor class to - construct the transform iterator type. The nicest way to package - up the construction of the transform iterator is to create a iterator_adaptor template to construct + the transform iterator type. The nicest way to package the construction of + the transform iterator is to create a type - generator. The first template parameter of the generator will - be the type of the function object and the second will be the base - iterator type. Inside the transform_iterator_generator - class we use the iterator_adaptor class to create the - transform iterator type. + generator. The first template parameter to the generator will be the + type of the function object and the second will be the base iterator + type. We use + iterator_adaptor to define the transform iterator type as a nested + typedef inside the transform_iterator_generator class. Because the function may return by-value, we must limit the + iterator_category to Input Iterator, + and the iterator's reference type cannot be a true reference (the + standard allows this for input iterators), so in this case we can use few of + iterator_adaptor's default template arguments.
@@ -333,9 +419,9 @@ make_transform_iterator(Iterator base,-
The following is an example of how to use a transform iterator +
Here is an example that shows how to use a transform iterator to iterate through a range of numbers, multiplying each of them by - 2 when they are dereferenced and printing the result to standard + 2 and printing the result to standard output.
@@ -364,68 +450,54 @@ This output is: +
C++ allows const and + non-const pointers to interact in the following intuitive ways: +
Parameter
+ Getting user-defined iterators to work together that way is nontrivial, but
+ iterator_adaptor can make it easy. The rules are as follows:
+
+
|
---|
There is an unlimited number of ways the the + iterator_adaptors class can be used to create + iterators. One interesting exercise would be to re-implement the + iterators of std::list and std::slist using + iterator_adaptors, where the adapted Iterator + types would be node pointers. -
+template <class Base, class Policies, + class Value = typename std::iterator_traits<Base>::value_type, + class Reference = ...(see below), + class Pointer = ...(see below), + class Category = typename std::iterator_traits<Base>::iterator_category, + class Distance = typename std::iterator_traits<Base>::difference_type + > +struct iterator_adaptor +{ + typedef Distance difference_type; + typedef typename boost::remove_const<Value>::type value_type; + typedef Pointer pointer; + typedef Reference reference; + typedef Category iterator_category; + typedef Base base_type; + typedef Policies policies_type; --
-iterator_adaptor(const Base&, const Policies& = Policies())
- -Construct an iterator adaptor from a base object and a policies object. - |
-template <class B, class V, class R, class P> -iterator_adaptor(const iterator_adaptor<B,Policies,V,R,P,Category,Distance>&) - -This constructor allows for conversion from mutable to constant iterator -adaptors. This assumes that the type B is convertible to the -type Base. - |
-base_type base() const;
- -Return a copy of the base object. - |
There is an unlimited number of ways the the - iterator_adaptors class can be used to create - iterators. One interesting exercise would be to re-implement the - iterators of std::list and std::slist using - iterator_adaptors, where the adapted Iterator - types would be node pointers. + iterator_adaptor& operator+=(difference_type n); + iterator_adaptor& operator-=(difference_type n); -
[1] If your compiler does not support partial specialization and the base iterator is a builtin pointer type, then you will not be able to use the default for Value and will need to explicitly specify this type. -
[2] The result type for the operator->() +
[2] The standard specifies that the value_type + of const iterators to T (e.g. const T*) is + non-const T, while the pointer and + reference types for all Forward + Iterators are const T* and const T&, + respectively. Stripping the const-ness of Value is + designed to allow you to easily make a const iterator adaptor by + supplying a const type for Value, and allowing the + defaults for the Pointer and Reference parameters to take + effect. Although compilers that don't support partial specialization won't + do this for you, having a const value_type is often harmless in + practice. + +
[3] The result type for the operator->() depends on the category and value type of the iterator and is somewhat complicated to describe. But be assured, it works in a stardard conforming fashion, providing access to members of the objects pointed to by the iterator. -
[4] The result type of operator[]() is value_type + instead of reference as might be expected. There are two + reasons for this choice. First, the C++ standard only requires that the return + type of an arbitrary + + Random Access Iterator's operator[]be ``convertible to T'' + (Table 76), so when adapting an arbitrary base iterator we may not have a + reference to return. Second, and more importantly, for certain kinds of + iterators, returning a reference could cause serious memory problems due to + the reference being bound to a temporary object whose lifetime ends inside + of the operator[]. -
Revised - 10 + 15 Feb 2001
© Copyright Dave Abrahams and Jeremy Siek 2001. Permission to copy, use, modify, sell