diff --git a/iterator_adaptors.htm b/iterator_adaptors.htm index 976870a..fe6cae2 100644 --- a/iterator_adaptors.htm +++ b/iterator_adaptors.htm @@ -1,11 +1,10 @@ + - - Boost Iterator Adaptor Library c++boost.gif (8819 bytes)Introduction -

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. +

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.

Table of Contents

@@ -32,35 +30,49 @@
  • Header Dave Abrahams started the library, applying policy class technique - and handling const/non-const iterator interactions. He also contributed the + "../../more/generic_programming.html#policy">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 @@ -91,7 +103,8 @@ "#iterator_adaptor">iterator_adaptor template.
    -

    Class template iterator_adaptor

    +

    Class template + iterator_adaptor

    Implementing standard conforming iterators is a non-trivial task. There are some fine points such as the interactions between an iterator and its corresponding const_iterator, and there are myriad operators that should be @@ -116,46 +129,48 @@ struct iterator_adaptor;

    Template Parameters

    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. + 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 + BaseType The type being wrapped.
    Policies + Policies - A policy class - that supplies core functionality to the resulting iterator. A + A policy + class that supplies core functionality to the resulting iterator. A detailed description can be found below.
    Value + 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].
    +
    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 + std::iterator_traits<BaseType>::value_type
    Pointer + Pointer The pointer type of the resulting iterator, and in particular, the result type of operator->().
    - Default: If Value was supplied, then Value*, + Default: If Value was supplied, then Value*, otherwise std::iterator_traits<BaseType>::pointer.
    Reference + Reference The reference type of the resulting iterator, and in particular, the result type of operator*().
    @@ -164,84 +179,113 @@ struct iterator_adaptor; std::iterator_traits<BaseType>::reference is used.
    Category + Category The iterator_category type for the resulting iterator.
    - Default: - std::iterator_traits<BaseType>::iterator_category + Default: + std::iterator_traits<BaseType>::iterator_category
    Distance + Distance The difference_type for the resulting iterator.
    - Default: - std::iterator_traits<BaseType>::difference_type + Default: + std::iterator_traits<BaseType>::difference_type

    The Policies Class

    The main task in using iterator_adaptor is creating an - appropriate Policies class. + 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. - 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. + + - - -
    + Core Iterator Operations +
    - - - + + + + + + +
    Core Iterator Operations
    Operation Effects Required for Iterator Categories
    dereference returns an element of the iterator's - reference type Input/ Output/ Forward/ Bidirectional/ Random Access + Operation -
    equal tests the iterator for equality + Effects -
    increment increments the iterator + Required for Iterator Categories -
    decrement decrements the iterator Bidirectional/ Random Access +
    dereference -
    less imposes a Strict Weak - Ordering relation on the iterator's reference type - Random - Access + returns an element of the iterator's reference type -
    distance measures the distance between iterators + Input/ Output/ Forward/ Bidirectional/ + Random + Access -
    advance adds an integer offset to iterators - +
    equal + + tests the iterator for equality + +
    increment + + increments the iterator + +
    decrement + + decrements the iterator + + Bidirectional/ + Random + Access + +
    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, +

    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 + 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: + policy classes, defining functions with the same interface. This is the + definition of default_iterator_policies:
    +
    -

    -

    +
     struct default_iterator_policies
     {
    @@ -274,82 +318,79 @@ struct default_iterator_policies
         { 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. +

    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.

    Additional Members

    + In addition to all of the member functions required of a Random Access + Iterator, the iterator_adaptor class template defines the + following members.
    +
    + - In addition to all of the member functions required of a - Random Access Iterator, the iterator_adaptor class - template defines the following members. + + + +
    iterator_adaptor(const Base&, const Policies& = + Policies())
    + Construct an adapted iterator from a base object and a policies + object. -

    - - + + - - - -
    -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. -
    -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. -
    - +

    base_type base() const;
    +
    + Return a copy of the base object. +

    Example

    -

    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.

    +

    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. + 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 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:
    +
    - 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 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
    @@ -366,26 +407,26 @@ type.  The complete code for transform_iterator_policies is:
         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 +

    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. + 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
    @@ -398,14 +439,16 @@ public:
           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. -

    -

    +

    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
    @@ -417,15 +460,15 @@ make_transform_iterator(Iterator base,
         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. +

    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>
    @@ -438,78 +481,75 @@ int main(int, char*[])
       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, " "));
    +      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: + This output is:
     2 4 6 8 10 12 14 16
     
    -
    +

    Iterator Interactions

    -

    C++ allows const and - non-const pointers to interact in the following intuitive ways: +

    C++ allows const and non-const pointers to interact in + the following intuitive ways: +

    - Getting user-defined iterators to work together that way is nontrivial, but - iterator_adaptor can make it easy. The rules are as follows: - + iterator_adaptor can make it easy. The rules are as follows: +

    Challenge

    -

    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. - - -

    Model of

    - - Depending on the Base and Policies template - parameters, an iterator_adaptor can be a Input - Iterator, Forward - Iterator, - Bidirectional Iterator, or - Random Access Iterator. +

    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. +

    Concept Model

    + Depending on the Base and Policies template parameters, + an iterator_adaptor can be a Input Iterator, Forward + Iterator, Bidirectional + Iterator, or Random Access + Iterator.

    Declaration Synopsis

    @@ -540,7 +580,8 @@ struct iterator_adaptor
             const iterator_adaptor<B,Policies,V,R,P,Category,Distance>&);
     
         reference operator*() const;
    -    operator_arrow_result_type operator->() const; [3]
    +    operator_arrow_result_type operator->() const; [3]
         value_type operator[](difference_type n) const; [4]
     
         iterator_adaptor& operator++();
    @@ -574,68 +615,72 @@ template <class B1, class B2, class P, class V1, class V2,
     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 !=, <, <=, >=, >
    +// and similarly for operators !=, <, <=, >=, >
     

    Notes

    [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. + 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 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[]. + 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 15 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. - - - - - - +

    © 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. + + + + + + + + + + +