In [iterator.concept.forward], std::forward_iterator
is defined as:
template<class I>
concept forward_iterator =
input_iterator<I> &&
derived_from<ITER_CONCEPT(I), forward_iterator_tag> &&
incrementable<I> &&
sentinel_for<I, I>;
and std::sentinel_for<I, I>
is defined as:
template<class S, class I>
concept sentinel_for =
semiregular<S> &&
input_or_output_iterator<I> &&
weakly-equality-comparable-with<S, I>;
which seems to be redundant in std::forward_iterator
's requirement, since input_iterator
already models input_or_output_iterator
, and incrementable
is defined as:
template<class I>
concept incrementable =
regular<I> &&
weakly_incrementable<I> &&
requires(I i) {
{ i++ } -> same_as<I>;
};
which models regular
which models semiregular
and equality_comparable
which models weakly-equality-comparable-with<I, I>
.
Why does the standard need to add a constraint that seems redundant here, and what are the considerations behind this?
forward_iterator
needs the semantic requirements of sentinel_for
. Those are not implied by either regular
or equality_comparable
.
Concepts have explicit syntactical requirements (ie: these expressions have to compile), but they also have implicit semantic concepts. equality_comparable<T>
has a semantic requirement that if t == u
, then t
and u
have the same value, however that is defined for T
.
But sentinel_for
requires a more specific meaning for t == u
. Equality between a sentinel and an iterator means that the iterator is at the end of the range (and therefore, you aren't allowed to dereference it).
So these concepts are not redundant; they are needed to express the semantic meaning of forward_iterator<T>
. In this case, that testing one iterator against another is a valid way to check for the end of the sequence. And therefore, the meaning of "value" for a forward iterator is "position in the range".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With