ranges::advance(i, s)
Section: 24.4.4.2 [range.iter.op.advance], 24.3.4.7 [iterator.concept.sentinel] Status: C++23 Submitter: Casey Carter Opened: 2020-06-18 Last modified: 2023-11-22
Priority: 2
View all other issues in [range.iter.op.advance].
View all issues with C++23 status.
Discussion:
The specification of the iterator & sentinel overload of ranges::advance
in 24.4.4.2 [range.iter.op.advance] reads:
template<input_or_output_iterator I, sentinel_for<I> S> constexpr void ranges::advance(I& i, S bound);-3- Preconditions:
-4- Effects:[i, bound)
denotes a range.
(4.1) — If
I
andS
modelassignable_from<I&, S>
, equivalent toi = std::move(bound)
.(4.2) — […]
The assignment optimization in bullet 4.1 is just fine for callers with concrete types who can decide whether
or not to call advance
depending on the semantics of the assignment performed. However, since this assignment
operation isn't part of the input_or_output_iterator
or sentinel_for
requirements its semantics
are unknown for arbitrary types. Effectively, generic code is forbidden to call this overload of advance
when
assignable_from<I&, S>
is satisfied and non-generic code must tread lightly. This seems to
make the library dangerously unusable.
We can correct this problem by either:
Making the assignment operation in question an optional part of the sentinel_for
concept with well-defined
semantics. This concept change should be relatively safe given that assignable_from<I&, S>
requires common_reference_with<const I&, const S&>
, which is very rarely satisfied
inadvertently.
Requiring instead same_as<I, S>
to trigger the assignment optimization in bullet 4.1 above.
S
is semiregular
, so i = std::move(s)
is certainly well-formed (and has well-defined
semantics thanks to semiregular
) when I
and S
are the same type. The optimization will
not apply in as many cases, but we don't need to make a scary concept change, either.
[2020-06-26; Reflector prioritization]
Set priority to 2 after reflector discussions.
[2020-08-21; Issue processing telecon: Option A is Tentatively Ready]
Previous resolution [SUPERSEDED]:
This wording is relative to N4861.
Wording for both Option A and Option B are provided.
Option A:
Modify 24.3.4.7 [iterator.concept.sentinel] as indicated:
template<class S, class I> concept sentinel_for = semiregular<S> && input_or_output_iterator<I> && weakly-equality-comparable-with<S, I>; // See 18.5.4 [concept.equalitycomparable]-2- Let
s
andi
be values of typeS
andI
such that[i, s)
denotes a range. TypesS
andI
modelsentinel_for<S, I>
only if
(2.1) —
i == s
is well-defined.(2.2) — If
bool(i != s)
theni
is dereferenceable and[++i, s)
denotes a range.(2.?) —
assignable_from<I&, S>
is either modeled or not satisfied.Option B:
Modify 24.4.4.2 [range.iter.op.advance] as indicated:
template<input_or_output_iterator I, sentinel_for<I> S> constexpr void ranges::advance(I& i, S bound);-3- Preconditions:
-4- Effects:[i, bound)
denotes a range.
(4.1) — If
I
andS
model, equivalent to
assignable_from<I&, S>same_as<I, S>i = std::move(bound)
.(4.2) — […]
[2020-11-09 Approved In November virtual meeting. Status changed: Tentatively Ready → WP.]
Proposed resolution:
This wording is relative to N4861.
Modify 24.3.4.7 [iterator.concept.sentinel] as indicated:
template<class S, class I> concept sentinel_for = semiregular<S> && input_or_output_iterator<I> && weakly-equality-comparable-with<S, I>; // See 18.5.4 [concept.equalitycomparable]-2- Let
s
andi
be values of typeS
andI
such that[i, s)
denotes a range. TypesS
andI
modelsentinel_for<S, I>
only if
(2.1) —
i == s
is well-defined.(2.2) — If
bool(i != s)
theni
is dereferenceable and[++i, s)
denotes a range.(2.?) —
assignable_from<I&, S>
is either modeled or not satisfied.