diff --git a/iterator_adaptors.htm b/iterator_adaptors.htm new file mode 100644 index 0000000..aa3c42a --- /dev/null +++ b/iterator_adaptors.htm @@ -0,0 +1,892 @@ + + + +
+ + + + + +The Iterator Adaptor library allows you transform an arbitrary ``base'' + type into a standard-conforming iterator with the behaviors you choose. + Doing so is especially easy if the ``base'' type is itself an iterator. The + library also supplies several example adaptors which apply + specific useful behaviors to arbitrary base iterators. + +
Dave
+ Abrahams started the library, applying policy class technique and
+ handling const/non-const iterator interactions. He also contributed the
+ indirect_ and reverse_ iterator generators, and expanded
+ counting_iterator_generator to
+ cover all incrementable types. He edited most of the documentation,
+ sometimes heavily.
+ Jeremy
+ Siek contributed the transform
+ iterator adaptor, the integer-only version of counting_iterator_generator,
+ the function output iterator
+ adaptor, and most of the documentation.
+ John
+ Potter contributed the projection_ and filter_ iterator generators and made some
+ simplifications to the main iterator_adaptor template.
+
+
+
iterator_adaptor is declared like this: +
+template <class Base, class Policies, + class ValueOrNamedParam = typename std::iterator_traits<Base>::value_type, + class ReferenceOrNamedParam = ...(see below), + class PointerOrNamedParam = ...(see below), + class CategoryOrNamedParam = typename std::iterator_traits<Base>::iterator_category, + class DistanceOrNamedParam = typename std::iterator_traits<Base>::difference_type> +struct iterator_adaptor; ++ +
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 the
+ value_type will be (non-const) X[1]. If the value_type you wish to use is an abstract
+ base class see note [5]. + Default: + std::iterator_traits<BaseType>::value_type [2] + + |
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. + + |
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. + + |
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 + + |
NamedParam + + | A named template parameter (see below). + |
++ + For example, the following adapts foo_iterator to create + an InputIterator + with reference type foo, and whose other traits + are determined according to the defaults described above. + ++template <class Value> struct value_type_is; +template <class Reference> struct reference_is; +template <class Pointer> struct pointer_is; +template <class Distance> struct difference_type_is; +template <class Category> struct iterator_category_is; ++
++ + ++typedef iterator_adaptor<foo_iterator, foo_policies, + reference_is<foo>, iterator_category_is<std::input_iterator_tag> + > MyIterator; ++
The main task in using iterator_adaptor is creating an
+ appropriate Policies class. The Policies class will become
+ the functional heart of the resulting iterator, supplying the core
+ operations that determine its behavior. The iterator_adaptor
+ template defines all of the operators required of a Random Access
+ Iterator by dispatching to a Policies object. Your
+ Policies class must implement a subset of the core iterator
+ operations below corresponding to the iterator categories you want it to
+ support.
+
+
+
+
Operation + + | Effects + + | Implements Operations + + | Required for Iterator Categories + + |
---|---|---|---|
initialize + + | optionally modify base iterator during iterator construction + + | constructors + + | Input/ Output/ Forward/ Bidirectional/ + Random + Access + + + |
dereference + + | returns an element of the iterator's reference type + + | *p, p[n] + + + | |
equal + + | tests the iterator for equality + + | p1 == p2, p1 != p2 + + | |
increment + + | increments the iterator + + | ++p, p++ + + | |
decrement + + | decrements the iterator + + | --p, p-- + + | Bidirectional/ + Random + Access + + |
less + + | imposes a Strict Weak + Ordering relation on iterators + + | + p1 < p2, + p1 <= p2, + p1 > p2, + p1 >= p2 + + | Random + Access + + |
distance + + | measures the distance between iterators + + | p1 - p2 + + | |
advance + + | adds an integer offset to iterators + + | +p + x, +x + p, +p += x, +p - x, +p -= x + + |
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 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 ``boilerplate'' for your own
+ policy classes, defining functions with the same interface. This is the
+ definition of default_iterator_policies:
+
+
+
++ ++struct default_iterator_policies +{ + template <class BaseType> + void initialize(BaseType&) + { } + + template <class Reference, class BaseType> + Reference dereference(type<Reference>, const BaseType& x) const + { return *x; } + + template <class BaseType> + void increment(BaseType& x) + { ++x; } + + template <class BaseType1, class BaseType2> + bool equal(const BaseType1& x, const BaseType2& y) const + { return x == y; } + + template <class BaseType> + void decrement(BaseType& x) + { --x; } + + template <class BaseType, class DifferenceType> + void advance(BaseType& x, DifferenceType n) + { x += n; } + + template <class Difference, class BaseType1, class BaseType2> + Difference distance(type<Difference>, const BaseType1& x, const BaseType2& y) const + { return y - x; } + + template <class BaseType1, class BaseType2> + bool less(const BaseType1& x, const 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 can use them to generate more specialized + adaptors along the lines of those supplied by this library. + +
explicit iterator_adaptor(const Base&, const Policies& =
+ Policies())
+ + Construct an adapted iterator from a base object and a policies + object. As this constructor is explicit, it does not + provide for implicit conversions from the Base type to + the iterator adaptor. + + |
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 can
+ inherit the rest from default_iterator_policies. We will define
+ the dereference() member function, which is used to 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:
+
+
+
+
++ ++ template <class AdaptableUnaryFunction> + struct transform_iterator_policies : public default_iterator_policies + { + transform_iterator_policies() { } + + transform_iterator_policies(const AdaptableUnaryFunction& f) + : m_f(f) { } + + template <class Reference, class BaseIterator> + Reference dereference(type<Reference>, const BaseIterator& i) const + { return m_f(*i); } + + AdaptableUnaryFunction m_f; + }; ++
The next step is to use the 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 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.
+
+
+
+
++ ++template <class AdaptableUnaryFunction, class Iterator> +struct transform_iterator_generator +{ + typedef typename AdaptableUnaryFunction::result_type value_type; +public: + typedef iterator_adaptor<Iterator, + transform_iterator_policies<AdaptableUnaryFunction>, + value_type, value_type, value_type*, std::input_iterator_tag> + type; +}; ++
As a finishing touch, we will create an object generator
+ for the transform iterator. This is a function that makes it more
+ convenient to create a transform iterator.
+
+
+
+
++ ++template <class AdaptableUnaryFunction, class Iterator> +typename transform_iterator_generator<AdaptableUnaryFunction,Iterator>::type +make_transform_iterator(Iterator base, + const AdaptableUnaryFunction& f = AdaptableUnaryFunction()) +{ + typedef typename transform_iterator_generator<AdaptableUnaryFunction, + Iterator>::type result_t; + return result_t(base, f); +} ++
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 and printing the
+ result to standard output.
+
+
+
+
++ ++#include <functional> +#include <algorithm> +#include <iostream> +#include <boost/iterator_adaptors.hpp> + +int main(int, char*[]) +{ + int x[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + const int N = sizeof(x)/sizeof(int); + std::cout << "multiplying the array by 2:" << std::endl; + std::copy(boost::make_transform_iterator(x, std::bind1st(std::multiplies<int>(), 2)), + boost::make_transform_iterator(x + N, std::bind1st(std::multiplies<int>(), 2)), + std::ostream_iterator<int>(std::cout, " ")); + std::cout << std::endl; + return 0; +} ++ This output is: ++2 4 6 8 10 12 14 16 ++
C++ allows const and non-const pointers to interact in + the following intuitive ways: + +
The Projection Iterator adaptor is similar to the transform iterator adaptor in that +its operator*() applies some function to the result of +dereferencing the base iterator and then returns the result. The +difference is that the function must return a reference to some +existing object (for example, a data member within the +value_type of the base iterator). + +
+The projection_iterator_pair_generator template + is a special two-type generator for mutable and constant versions of a + projection iterator. It is defined as follows: +
++ ++template <class AdaptableUnaryFunction, class Iterator, class ConstIterator> +struct projection_iterator_pair_generator { + typedef typename AdaptableUnaryFunction::result_type value_type; + typedef projection_iterator_policies<AdaptableUnaryFunction> policies; +public: + typedef iterator_adaptor<Iterator,policies,value_type> iterator; + typedef iterator_adaptor<ConstIterator,policies,value_type, + const value_type&,const value_type*> const_iterator; +}; ++
It is assumed that the Iterator and ConstIterator arguments are corresponding mutable +and constant iterators.
There is an unlimited number of ways 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(); + explicit 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; [6] + operator_arrow_result_type operator->() const; [3] + value_type operator[](difference_type n) const; [4], [6] + + 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 !=, <, <=, >=, > ++ +
[1] 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 allows 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 strip const for you, having a + const value_type is often harmless in practice. + +
[2] If your compiler does not support partial + specialization and the base iterator is a builtin pointer type, you + will not be able to use the default for Value and will have to + specify this type explicitly. + +
[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[]. + +
[5] + The value_type of an iterator may not be + an abstract base class, however many common uses of iterators + never need the value_type, only the reference type. + If you wish to create such an iterator adaptor, use a dummy + type such as char for the Value parameter, + and use a reference to your abstract base class for + the Reference parameter. Note that such an iterator + does not fulfill the C++ standards requirements for a + + Forward Iterator, so you will need to use a less restrictive + iterator category such as std::input_iterator_tag. + +
[6] + There is a common misconception that an iterator should have two + versions of operator* and of operator[], one + version that is a const member function and one version + that is non-const. Perhaps the source of this + misconception is that containers typically have const and + non-const versions of many of their member functions. Iterators, + however, are different. A particular iterator type can be either + mutable or constant (but not both). One can assign + to and change the object pointed to by a mutable iterator whereas a + constant iterator returns constant objects when dereferenced. Whether + the iterator object itself is const has nothing to do with + whether the iterator is mutable or constant. This is analogous to + the way built-in pointer types behave. For example, one can + modify objects pointed to by a const pointer +
+ int* const x = new int; + int i = 3; + *x = i; ++ but one cannot modify objects pointed to by a pointer + to const +
+ int const* x = new int; + int i = 3; + *x = i; ++ +
Revised + 18 Sep 2001 + + +
© Copyright Dave Abrahams and Jeremy Siek 2001. Permission to copy, + use, modify, sell and distribute this document is granted provided this + copyright notice appears in all copies. This document is provided "as is" + without express or implied warranty, and with no claim as to its + suitability for any purpose. + + + + + + + + + + + + + + +