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 ValueOrNamedParams = typename std::iterator_traits<Base>::value_type, class ReferenceOrNamedParams = ...(see below), class PointerOrNamedParams = ...(see below), class CategoryOrNamedParams = typename std::iterator_traits<Base>::iterator_category, class DistanceOrNamedParams = 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]. 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 |
NamedParams | A list of named template parameters generated using the iterator_traits_generator class (see below). |
The iterator_traits_generator is used to create a list of of template arguments. For example, suppose you want to set the Reference and Category parameters, and use the defaults for the rest. Then you can use the traits generator as follows:class iterator_traits_generator { public: template <class Value> struct value_type : public recursive magic { }; template <class Reference> struct reference : public recursive magic { }; template <class Pointer> struct pointer : public recursive magic { }; template <class Distance> struct difference_type : public recursive magic { }; template <class Category> struct iterator_category : public recursive magic { }; };
This generated type can then be passed into the iterator_adaptor class to replace any of the last five parameters. If you use the traits generator in the ith parameter position, then the parameters i through 7 will use the types specified in the generator.iterator_traits_generator::reference<foo>::category<std::input_iterator_tag>
iterator_adaptor<foo_iterator, foo_policies, iterator_traits_generator ::reference<foo> ::category<std::input_iterator_tag> >
The main task in using iterator_adaptor is creating an
appropriate Policies class. 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.
Operation | Effects | Implements Operations | Required for Iterator Categories |
---|---|---|---|
dereference | returns an element of the iterator's reference type | *p, p[n] | Input/ Output/ Forward/ Bidirectional/ Random Access |
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 Reference, class BaseType> Reference dereference(type<Reference>, const BaseType& x) const { return *x; } template <class BaseType> static void increment(BaseType& x) { ++x; } template <class BaseType1, class BaseType2> bool equal(BaseType1& x, BaseType2& y) const { return x == y; } template <class BaseType> static void decrement(BaseType& x) { --x; } template <class BaseType, class DifferenceType> static void advance(BaseType& x, DifferenceType n) { x += n; } template <class Difference, class BaseType1, class BaseType2> Difference distance(type<Difference>, BaseType1& x, BaseType2& y) const { return y - x; } 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.
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; operator_arrow_result_type operator->() const; [3] value_type operator[](difference_type n) const; [4] 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[].
Revised 28 Feb 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.