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 policies 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.
Jeremy
Siek contributed the transform
iterator adaptor, the integer-only version of counting_iterator_generator, and most of
the documentation.
John
Potter contributed the projection_ and filter_ iterator generators and made some
simplifications to the main iterator_adaptor template.
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; iterator_adaptor(); iterator_adaptor(const Base&, const Policies& = Policies()); base_type base() const; template <class Iter2, class Value2, class Pointer2, class Reference2> iterator_adaptor ( const iterator_adaptor<Iter2,Policies,Value2,Reference2,Pointer2,Category,Distance>& : m_iter_p(src.iter(), src.policies()); 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 Policies, class V, class R, class P, class C, class D1, class D2> iterator_adaptor<B,Policies,V,R,P,C,D1> operator+(iterator_adaptor<B,P,V,R,P,C,D1>, D2); template <class B, class Policies, class V, class R, class P, class C, class D1, class D2> iterator_adaptor<B,Policies,V,R,P,C,D1> operator+(D2, iterator_adaptor<B,P,V,R,P,C,D1> p); template <class B1, class B2, class Policies, 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 Policies, 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>&); template <class B1, class B2, class Policies, 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>&); template <class B1, class B2, class Policies, 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>&); template <class B1, class B2, class Policies, 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>&); template <class B1, class B2, class Policies, 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>&); template <class B1, class B2, class Policies, 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>&);
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.
The main task in using iterator_adaptor is creating an 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:
dereference
- returns an element of the iterator's
reference
type
equal
- tests the iterator for equality
increment
- increments the iterator
decrement
- decrements bidirectional and random-access
iterators
less
- imposes a strict weak ordering relation on
random-access iterators
distance
- measures the distance between random-access
iterators
advance
- adds an integer offset to random-access
iterators
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 (set of requirements) 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.struct default_iterator_policies { // required for a ForwardIterator template <class Reference, class Iterator> Reference dereference(type<Reference>, const Iterator& x) const { return *x; } template <class Iterator> static void increment(Iterator& x) { ++x; } template <class Iterator1, class Iterator2> bool equal(Iterator1& x, Iterator2& y) const { return x == y; } // required for a BidirectionalIterator template <class Iterator> static void decrement(Iterator& x) { --x; } // required for a RandomAccessIterator template <class Iterator, class DifferenceType> static void advance(Iterator& x, DifferenceType n) { x += n; } template <class Difference, class Iterator1, class Iterator2> Difference distance(type<Difference>, Iterator1& x, Iterator2& y) const { return y - x; } template <class Iterator1, class Iterator2> bool less(Iterator1& x, Iterator2& y) const { return x < y; } };
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 class to construct the transform iterator type. The nicest way to package up the construction of the transform iterator is to create a type generator, which is a class whose sole purpose is to create a typedef for some new type based on several template parameters. The first template parameter will be the type of the function object and the second will be the base iterator type. Inside the transform_iterators class we use the iterator_adaptor class to create the transform iterator type.
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); }
The following is an example of 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 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; }
Parameter | Description |
---|---|
Predicate | The function object that determines which elements are retained and which elements are skipped. |
BaseIterator | The iterator type being wrapped. This type must at least be a model of the InputIterator concept. |
Value | The value_type of the resulting iterator, unless const. If
const, a conforming compiler strips constness for the
value_type. Typically the default for this parameter is the
appropriate type[1]. Default: std::iterator_traits<BaseIterator>::value_type |
Pointer | The pointer type of the resulting iterator, and in
particular, the result type of operator->(). Typically the default
for this parameter is the appropriate type. Default: If Value was supplied, then Value*, otherwise std::iterator_traits<BaseIterator>::pointer. |
Reference | The reference type of the resulting iterator, and in
particular, the result type of operator*(). Typically the default for
this parameter is the appropriate type. Default: If Value is supplied, Value& is used. Otherwise std::iterator_traits<BaseIterator>::reference is used. |
Category | The iterator_category type for the resulting iterator.
Typically the default for this parameter is the appropriate type. If
you override this parameter, do not use
bidirectional_iterator_tag because filter iterators can not go
in reverse. Default: std::iterator_traits<BaseIterator>::iterator_category |
Distance | The difference_type for the resulting iterator. Typically
the default for this parameter is the appropriate type. Default: std::iterator_traits<BaseIterator>::difference_type |
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.
[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->() 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.
Revised 10 Feb 2001
© Copyright Jeremy Siek and Dave Abrahams 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.