mirror of
https://github.com/boostorg/stl_interfaces.git
synced 2025-05-12 14:11:41 +00:00
226 lines
7.4 KiB
Plaintext
226 lines
7.4 KiB
Plaintext
[section Tutorial]
|
|
|
|
[heading The _iter_facade_ 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-supplied member functions. By deriving `Iter` from _iter_facade_ using
|
|
_CRTP_, we can generate the full set of operations. Here is the declaration
|
|
of _iter_facade_:
|
|
|
|
[iterator_facade_decl]
|
|
|
|
Let's break that down.
|
|
|
|
`Derived` is the type that you're deriving _iter_facade_ 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 `Reference` does not need to be a reference type, and `Pointer` does not
|
|
need to be a pointer type. This fact is very useful when making proxy
|
|
iterators. ]
|
|
|
|
`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`.
|
|
|
|
When defining a proxy iterator, you can use a template alias that provides
|
|
reasonable defaults for _iter_facade_'s parameters:
|
|
|
|
[proxy_iterator_alias_decl]
|
|
|
|
|
|
[heading User-Defined Iterator Operations]
|
|
|
|
Now, let's get back to the user-defined basis members.
|
|
|
|
In the table below, `Iter` is a user-defined type derived from _iter_facade_;
|
|
`i` and `i2` are objects of type `Iter`; `ci` is an object of type `Iter
|
|
const`; `reference` is the type passed as the `Reference` template parameter
|
|
to _iter_facade_; `pointer` is the type passed as the `Pointer` template
|
|
parameter to _iter_facade_; and `n` is a value of type `difference_type`.
|
|
|
|
[table User-Supplied Operations
|
|
[[Expression] [Return Type] [Semantics] [Assertion/note/pre-/post-condition]]
|
|
[
|
|
[ `ci.dereference()` ]
|
|
[ Convertible to `reference`. ]
|
|
[ Dereferences `ci` and returns the result. ]
|
|
[ ['Expects:] ci is dereferenceable. ]
|
|
]
|
|
[
|
|
[ `ci.equals(i2)` ]
|
|
[ Contextually convertible to `bool`. ]
|
|
[ Returns true if and only if `ci` and `i2` refer to the same value. ]
|
|
[ ['Expects:] `(ci, i2)` is in the domain of `==`. ]
|
|
]
|
|
[
|
|
[ `ci.compare(i2)` ]
|
|
[ Convertible to `difference_type`. ]
|
|
[ Returns `n`. ]
|
|
[ ['Expects:] there exists a value `n` of type `difference_type` such that `a + n == b`.
|
|
`b == a + (b - a)`. ]
|
|
]
|
|
[
|
|
[ `i.next()` ]
|
|
[ `void` ]
|
|
[ Increments `i`. ]
|
|
[ ]
|
|
]
|
|
[
|
|
[ `i.prev()` ]
|
|
[ `void` ]
|
|
[ Decrements `i`. ]
|
|
[ ]
|
|
]
|
|
[
|
|
[ `i.advance(n)` ]
|
|
[ `void` ]
|
|
[
|
|
`` difference_type m = n;
|
|
if (m >= 0)
|
|
while (m--) ++i;
|
|
else
|
|
while (m++) --i;`` ]
|
|
[ ]
|
|
]
|
|
]
|
|
|
|
[tip The table above leaves a lot of implementation freedom. In `advance()`,
|
|
you could take `n` as a value or as a reference; `compare()` can return a
|
|
`difference_type` or just something convertible to one; etc. In particular,
|
|
your operations can be `constexpr` or `noexcept` as you see fit.]
|
|
|
|
Not all the iterator concepts require all the operations above. Here are the
|
|
operations used with each iterator cocept:
|
|
|
|
[table Operations Required for Each Concept
|
|
[[Concept] [Operations]]
|
|
[
|
|
[ `input_iterator` ]
|
|
[
|
|
``dereference()
|
|
equals()
|
|
next()`` ]
|
|
]
|
|
[
|
|
[ `output_iterator` ]
|
|
[
|
|
``dereference()
|
|
next()`` ]
|
|
]
|
|
[
|
|
[ `forward_iterator` ]
|
|
[
|
|
``dereference()
|
|
equals()
|
|
next()`` ]
|
|
]
|
|
[
|
|
[ `bidirectional_iterator` ]
|
|
[
|
|
``dereference()
|
|
equals()
|
|
next()
|
|
prev()`` ]
|
|
]
|
|
[
|
|
[ `random_access_iterator`/`continguous_iterator` ]
|
|
[
|
|
``dereference()
|
|
compare()
|
|
advance()`` ]
|
|
]
|
|
]
|
|
|
|
[heading Putting it All Together]
|
|
|
|
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:
|
|
|
|
[node_defn]
|
|
|
|
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.
|
|
|
|
[node_iterator_class_head]
|
|
|
|
We are deriving `node_iterator` from _iter_facade_, and because we're using
|
|
_CRTP_, we first have to pass `node_iterator` for the `Derived` template
|
|
parameter, so that _iter_facade_ 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 forward linked list. Finally, we pass `T` to let _iter_facade_ 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 const` for this reason.
|
|
|
|
[node_iterator_ctors]
|
|
|
|
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 _iter_facade_ cannot supply this, since constructors are not
|
|
visible in derived types without user intervention.
|
|
|
|
[important A default constructor is required for every iterator concept.]
|
|
|
|
[node_iterator_private]
|
|
|
|
Next, we define the user-defined operations that _iter_facade_ requires to do
|
|
its work. As you might expect, the three required operations are very
|
|
straightfoward.
|
|
|
|
[note Above I've made all of those private, and `friend`ed
|
|
`boost::iterator_facade::access`. This is recommended, but it is not strictly
|
|
necessary. If you make all of the user-defined operations public, there's no
|
|
need to `friend` `boost::iterator_facade::access`.]
|
|
|
|
Finally, we're ready to use our forward iterator:
|
|
|
|
[node_iterator_usage]
|
|
|
|
[heading Checking Your Work]
|
|
|
|
_Facade_ 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, _Facade_ does not know if your derived type includes a default
|
|
constructor, which is required by all the iterators. In particular,
|
|
_iter_facade_ cannot `static_assert` on the wellformedness of `Derived()`,
|
|
since `Derived` is an incomplete type within the body of _iter_facade_
|
|
_emdash_ _iter_facade_ is the base class for `Derived`, not the other way
|
|
round.
|
|
|
|
Since you can easily `static_assert` that a concept is satisfied by a
|
|
particular type, 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:
|
|
|
|
[node_iterator_concept_check]
|
|
|
|
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 corect types. See _traits_m_.
|
|
|
|
[endsect]
|