From 083b1b02df5015ff3ed2e3f95a611525f06d9ce5 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Fri, 16 Feb 2001 00:38:28 +0000 Subject: [PATCH] Bigtime edits [SVN r9219] --- iterator_adaptors.htm | 627 ++++++++++++++++++++++++------------------ 1 file changed, 362 insertions(+), 265 deletions(-) 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 @@
  • Header Dave Abrahams started the library, applying policies class technique + "../../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 - cover all incrementable types.
    + cover all incrementable types. He edited most of the documentation, + sometimes heavily.
    Jeremy Siek contributed the transform iterator adaptor, the integer-only version of const and non-const iterators. -

    Synopsis

    +

    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 !=, <, <=, >=, >
     
    -

    Example

    +

    Template Parameters

    -

    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 Policies Class

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

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

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

      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.

      + +

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

      Iterator Interactions

      -

      Template Parameters

      +

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

        +
      • a non-const pointer to T can be implicitly converted + to a const pointer to T. +
      • const and non-const pointers to T can be + freely mixed in comparison expressions. +
      • const and non-const pointers to T can be + freely subtracted, in any order. +
      - - - - - - - - -
      Parameter + Getting user-defined iterators to work together that way is nontrivial, but + iterator_adaptor can make it easy. The rules are as follows: + +
        +
      • Adapted iterators that share the same Policies, Category, and Distance + parameters are called interoperable. -
      Description +
    • An adapted iterator can be implicitly converted to any other adapted + iterator with which it is interoperable, so long as the Base type + of the source iterator can be converted to the Base type of the + target iterator. -
    • Base +
    • Interoperable iterators can be freely mixed in comparison expressions so long + as the Policies class has equal (and, for + random access iterators, + less) members that can accept both Base types in either + order. -
    • The type being wrapped. +
    • Interoperable iterators can be freely mixed in subtraction expressions so long + as the Policies class has a distance member that can + accept both Base types in either order. -
    • 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 +

      Challenge

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

      Model of

      +

      Model of

      Depending on the Base and Policies template parameters, an iterator_adaptor can be a Random Access Iterator. -

      Members

      - In addition to all of the member functions required of a - Random Access Iterator, the iterator_adaptor class - template defines the following members. +

      Declaration Synopsis

      +
      +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(); + 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>&); -
      -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. -
      - -

      Operators

      - - The iterator_adaptor class defines all of the operators - required of a - - Random Access Iterator. Also, the operators of the - iterator_adaptor class that take two iterator adaptor - arguments (difference and comparisons) are defined in such a way - that the operators work on pairs of iterator_adaptor - types that have different template parameters, provided that the - base types of the iterator adaptors are interoperable. The reason - for this is so that mutable and constant versions of an iterator - adaptor can be created that will be interoperable with eachother. + reference operator*() const; + operator_arrow_result_type operator->() const; [3] + value_type operator[](difference_type n) const; [4] -

      Challenge

      + iterator_adaptor& operator++(); + iterator_adaptor& operator++(int); + iterator_adaptor& operator--(); + iterator_adaptor& operator--(int); -

      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); -

      Notes

      + 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 !=, <, <=, >=, > +
      + +

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

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

      Implemenation Notes

      +

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

        -
      • The iterator_adaptor::operator[] returns by-value - instead of by-reference as might be expected. There are two - reasons for this. First, the C++ standard only requires that - the return type by ``convertible to T'' (Table 76). 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