|  | Home | Libraries | People | FAQ | More | 
| ![[Note]](../../../doc/src/images/note.png) | Note | 
|---|---|
| 
        All the member functions provided by  | 
iterator_interface Template
    Though a given iterator may have a large number of operations associated with it, there are only a few basis operations that the iterator needs to define; the full set of operations it supports can be defined in terms of that much smaller basis.
      It is possible to define any iterator Iter
      in terms of a subset of user-defined operations. By deriving Iter from iterator_interface using CRTP,
      we can generate the full set of operations. Here is the declaration of iterator_interface:
    
template< typename Derived, typename IteratorConcept, typename ValueType, typename Reference = ValueType &, typename Pointer = ValueType *, typename DifferenceType = std::ptrdiff_t> struct iterator_interface;
Let's break that down.
      Derived is the type that you're
      deriving iterator_interface from.
    
      IteratorConcept defines the
      iterator category/concept. This must be one of the C++ standard iterator tag
      types, like std::forward_iterator_tag. In C++20 and later,
      std::contiguous_iterator_tag is a valid tag to
      use.
    
      ValueType is used to define
      the iterator's value_type typedef.
      Likewise, Reference and Pointer are used to define the iterator's
      reference and pointer typedefs, respectively.
    
| ![[Tip]](../../../doc/src/images/tip.png) | Tip | 
|---|---|
| 
         | 
      DifferenceType is used to define
      the iterator's difference_type.
      Don't be a weirdo; just leave this as the default type, std::ptrdiff_t.
    
      Sometimes you need to create an iterator I
      such that I::reference_type is not a (possibly const) reference to I::value_type.
      For instance, let's say you want to make a zip-iterator that produces pairs
      of values from two separate underlying sequences. For sequences A and B,
      with respective value_types
      T and U,
      one possible reference_type
      for a zip iterator would be std::pair<T &, U &>
      (this is distinct from a reference to a pair, such as std::pair<T, U> &).
      Each such pair would contain a reference to one element from A and a reference to the corresponding element
      from B.
    
      As another example, if you wanted an iterator I
      that represents the infinite sequence 0, 1, 2, ..., you'd be unable to form
      a reference to most or all of those values; you'd instead produce a temporary
      for each value as it is needed. This implies that I::value_type
      does not involve references at all; it may instead by int
      or double.
    
      When defining a proxy iterator, you can use a template alias that provides
      reasonable defaults for iterator_interface's parameters:
    
template< typename Derived, typename IteratorConcept, typename ValueType, typename Reference = ValueType, typename DifferenceType = std::ptrdiff_t> using proxy_iterator_interface = iterator_interface< Derived, IteratorConcept, ValueType, Reference, proxy_arrow_result<Reference>, DifferenceType>;
| ![[Note]](../../../doc/src/images/note.png) | Note | 
|---|---|
| 
        As shown above,  | 
Now, let's get back to the user-defined basis operations.
      In the table below, Iter is
      a user-defined type derived from iterator_interface; i and i2
      are objects of type Iter;
      reference is the type passed
      as the Reference template parameter
      to iterator_interface; pointer is the type passed as the Pointer template parameter to iterator_interface;
      and n is a value of type difference_type.
    
Table 39.1. User-Defined Operations
| Expression | Return Type | Semantics | Assertion/note/pre-/post-condition | 
|---|---|---|---|
| 
                 | 
                Convertible to  | 
                Dereferences  | Expects: i is dereferenceable. | 
| 
                 | 
                Contextually convertible to  | 
                Returns true if and only if  | 
                Expects:  | 
| 
                 | 
                Convertible to  | 
                Returns  | 
                Expects: there exists a value  | 
| 
                 | 
                 | 
                Increments  | |
| 
                 | 
                 | 
                Decrements  | |
| 
                 | 
                 | 
 difference_type m = n; if (m >= 0) while (m--) ++i; else while (m++) --i; 
 | 
| ![[Tip]](../../../doc/src/images/tip.png) | Tip | 
|---|---|
| 
        The table above leaves a lot of implementation freedom. In  | 
Not all the iterator concepts require all the operations above. Here are the operations used with each iterator concept:
Table 39.2. Operations Required for Each Concept
| Concept | Operations | 
|---|---|
| 
                 | 
 *i i == i2 ++i 
 | 
| 
                 | 
 *i ++i 
 | 
| 
                 | 
 *i i == i2 ++i 
 | 
| 
                 | 
 *i i == i2 ++i --i 
 | 
| 
                 | 
 *i i - i2 i += n 
 | 
| ![[Note]](../../../doc/src/images/note.png) | Note | 
|---|---|
| 
        For  | 
operator++() and operator--()
    
      There's a wrinkle in this way of doing things. When you define operator++()
      in your iterator type Derived,
      iterator_interface defines post-increment,
      operator++(int). But since
      Derived has an operator++ and
      so does its base class iterator_interface, the one in
      Derived hides
      the one in iterator_interface.
    
      So, you need to add a using declaration that makes the operator++ from the base class visible in the derived
      class. For instance, in the node_iterator
      example there are these lines:
    
using base_type = boost::stl_interfaces:: iterator_interface<node_iterator<T>, std::forward_iterator_tag, T>; using base_type::operator++;
| ![[Important]](../../../doc/src/images/important.png) | Important | 
|---|---|
| 
        All of the above applies to  | 
| ![[Note]](../../../doc/src/images/note.png) | Note | 
|---|---|
| 
        These using declarations are not necessary for a random access iterator,
        because  | 
Ok, let's actually define a simple iterator. Let's say you need to interact with some legacy code that has a hand-written linked list:
template<typename T> struct node { T value_; node * next_; // == nullptr in the tail node };
      We can't change this code to use std::list, but
      it would be nice to be able to reuse all of the standard algorithms with this
      type. Defining an iterator will get us there.
    
template<typename T> struct node_iterator : boost::stl_interfaces:: iterator_interface<node_iterator<T>, std::forward_iterator_tag, T>
      We are deriving node_iterator
      from iterator_interface, and because
      we're using CRTP,
      we first have to pass node_iterator
      for the Derived template parameter,
      so that iterator_interface knows what
      derived type to cast to in order to get at the user-defined operations. Then,
      we pass std::forward_iterator_tag for IteratorConcept,
      since that's appropriate for a singly-linked list. Finally, we pass T to let iterator_interface know what
      the value_type is for our iterator.
    
      We leave the rest of the template parameters at their defaults: T & for
      Reference, T
      * for Pointer,
      and std::ptrdiff_t for DifferenceType.
      This is what you will do for almost all iterators. The most common exceptions
      to this are usually some kind of proxy iterator. Another exception is when
      for better code generation you want to return builtin values instead of references
      for constant iterators. To see an example of the latter, see the repeated_chars_iterator in the introduction;
      it's Reference template parameter
      is char for this reason.
    
constexpr node_iterator() noexcept : it_(nullptr) {} constexpr node_iterator(node<T> * it) noexcept : it_(it) {}
      Next, we define two constructors: a default constructor, and one that takes
      a node pointer. A default constructor
      is required by the forward_iterator
      concept, but iterator_interface cannot supply
      this, since constructors are not visible in derived types without user intervention.
    
| ![[Important]](../../../doc/src/images/important.png) | Important | 
|---|---|
| A default constructor is required for every iterator concept. | 
constexpr T & operator*() const noexcept { return it_->value_; } constexpr node_iterator & operator++() noexcept { it_ = it_->next_; return *this; } friend constexpr bool operator==(node_iterator lhs, node_iterator rhs) noexcept { return lhs.it_ == rhs.it_; }
      Next, we define the user-defined operations that iterator_interface requires to
      do its work. As you might expect, the three required operations are very straightforward.
    
| ![[Note]](../../../doc/src/images/note.png) | Note | 
|---|---|
| 
        Here, I implement  
 constexpr bool operator==(node_iterator rhs) const noexcept { return it_ == rhs.it_; } 
 
        Either of these forms works, since  | 
      Finally, we need a using declaration to make iterator_interface::operator++(int) visible:
    
using base_type = boost::stl_interfaces:: iterator_interface<node_iterator<T>, std::forward_iterator_tag, T>; using base_type::operator++;
Here's how we might use the forward iterator we just defined:
node_iterator<int> const first(&nodes[0]); node_iterator<int> const last; for (auto it = first; it != last; it++) { std::cout << *it << " "; // Prints 0 1 2 3 4 } std::cout << "\n";
So glad you asked. If you want to make something like a filtering iterator, or say a UTF-8 to UTF-32 transcoding iterator, you are starting with an existing iterator and adapting it. There's a way to avoid having to write all of the user-defined basis functions, as long as there's a base iterator that already has the right operations with the right semantics.
      For example, consider an iterator that contains a pointer to an array of int, and predicate of type Pred.
      It filters out integers that do not meet the predicate. Since we are using
      an existing iterator (the pointer to int),
      we already have all the operations we need for a bidirectional iterator (and
      more), except that operator++
      on an int *
      does not skip over elements as we'd like. Here's the code:
    
template<typename Pred> struct filtered_int_iterator : boost::stl_interfaces::iterator_interface< filtered_int_iterator<Pred>, std::bidirectional_iterator_tag, int> { filtered_int_iterator() : it_(nullptr) {} filtered_int_iterator(int * it, int * last, Pred pred) : it_(it), last_(last), pred_(std::move(pred)) { // We need to do this in the constructor so that operator== works // properly on two filtered_int_iterators, when they bound a sequence // in which none of the ints meets the predicate. it_ = std::find_if(it_, last_, pred_); } // A bidirectional iterator based on iterator_interface usually required // four user-defined operations. since we are adapting an existing // iterator (an int *), we only need to define this one. The others are // implemented by iterator_interface, using the underlying int *. filtered_int_iterator & operator++() { it_ = std::find_if(std::next(it_), last_, pred_); return *this; } // It is really common for iterator adaptors to have a base() member // function that returns the adapted iterator. int * base() const { return it_; } private: // Provide access to these private members. friend boost::stl_interfaces::access; // These functions are picked up by iterator_interface, and used to // implement any operations that you don't define above. They're not // called base() so that they do not collide with the base() member above. // // Note that the const overload does not strictly speaking need to be a // reference, as demonstrated here. constexpr int *& base_reference() noexcept { return it_; } constexpr int * base_reference() const noexcept { return it_; } int * it_; int * last_; Pred pred_; }; // A make-function makes it easier to deal with the Pred parameter. template<typename Pred> auto make_filtered_int_iterator(int * it, int * last, Pred pred) { return filtered_int_iterator<Pred>(it, last, std::move(pred)); }
      So, all we had to do was let iterator_interface know that
      there was an underlying iterator it could use — by implementing base_reference()
      — and the operations that we did not define got defined for us by iterator_interface.
    
Here is the iterator in action:
std::array<int, 8> ints = {{0, 1, 2, 3, 4, 5, 6, 7}}; int * const ints_first = ints.data(); int * const ints_last = ints.data() + ints.size(); auto even = [](int x) { return (x % 2) == 0; }; auto first = make_filtered_int_iterator(ints_first, ints_last, even); auto last = make_filtered_int_iterator(ints_last, ints_last, even); // This is an example only. Obviously, we could have called // std::copy_if() here. std::vector<int> ints_copy; std::copy(first, last, std::back_inserter(ints_copy)); assert(ints_copy == (std::vector<int>{0, 2, 4, 6}));
      Boost.STLInterfaces is able to check that some of the code that you write is
      compatible with the concept for the iterator you're writing. It cannot check
      everything. For instance, Boost.STLInterfaces does not know if your derived
      type includes a default constructor, which is required by all the iterators.
      In particular, iterator_interface cannot static_assert on the wellformedness of Derived(),
      since Derived is an incomplete
      type within the body of iterator_interface —
      iterator_interface is the base
      class for Derived, not the
      other way round.
    
      Since you can easily static_assert
      that a type models a given concept, a good practice is to put such a static_assert after you define your iterator
      type.
    
      For instance, after node_iterator
      you'll find this code:
    
// static_assert(std::forward_iterator<node_iterator>, ""), or nothing in // C++17 and earlier. BOOST_STL_INTERFACES_STATIC_ASSERT_CONCEPT( node_iterator<int>, std::forward_iterator)
Consider this good code hygiene. Without this simple check, you'll probably eventually find yourself looking at an error message with a very long template instantiation stack.
      There's also a macro that can help you check that std::iterator_traits
      is well-formed and provides the correct types. See BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_TRAITS.