This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++23 status.
3392.ranges::distance()
cannot be used on a move-only iterator with a sized sentinel
Section: 24.4.4.3 [range.iter.op.distance] Status: C++23 Submitter: Patrick Palka Opened: 2020-02-07 Last modified: 2023-11-22
Priority: 3
View all other issues in [range.iter.op.distance].
View all issues with C++23 status.
Discussion:
One cannot use ranges::distance(I, S)
to compute the distance between a move-only counted_iterator
and the default_sentinel
. In other words, the following is invalid
// iter is a counted_iterator with an move-only underlying iterator ranges::distance(iter, default_sentinel);
and yet
(default_sentinel - iter);
is valid. The first example is invalid because ranges::distance()
takes its first argument by value so when invoking it with an iterator lvalue argument the iterator must be copyable, which a move-only iterator is not. The second example is valid because counted_iterator::operator-()
takes its iterator argument by const
reference, so it doesn't require copyability of the counted_iterator
.
ranges::distance()
to efficiently compute the distance between two iterators or between an iterator-sentinel pair. Although it's a bit of an edge case, it would be good if ranges::distance()
does the right thing when the iterator is a move-only lvalue with a sized sentinel. If this is worth fixing, one solution might be to define a separate overload of ranges::distance(I, S)
that takes its arguments by const
reference, as follows.
[2020-02 Prioritized as P3 and LEWG Monday morning in Prague]
[2020-05-28; LEWG issue reviewing]
LEWG issue processing voted to accept the direction of 3392. Status change to Open.
Accept the direction of LWG3392 SF F N A SA 14 6 0 0 0
Previous resolution [SUPERSEDED]:
Modify 24.2 [iterator.synopsis], header
<iterator>
synopsis, as indicated:#include <concepts> namespace std { […] // 24.4.4 [range.iter.ops], range iterator operations namespace ranges { […] // 24.4.4.3 [range.iter.op.distance], ranges::distance template<input_or_output_iterator I, sentinel_for<I> S> requires (!sized_sentinel_for<S, I>) constexpr iter_difference_t<I> distance(I first, S last); template<input_or_output_iterator I, sized_sentinel_for<I> S> constexpr iter_difference_t<I> distance(const I& first, const S& last); template<range R> constexpr range_difference_t<R> distance(R&& r); […] } […] }Modify 24.4.4.3 [range.iter.op.distance] as indicated:
template<input_or_output_iterator I, sentinel_for<I> S> requires (!sized_sentinel_for<S, I>) constexpr iter_difference_t<I> ranges::distance(I first, S last);-1- Preconditions:
-2- Effects: If[first, last)
denotes a range , or[last, first)
denotes a range andS
andI
modelsame_as<S, I> && sized_sentinel_for<S, I>
.S
andI
modelsized_sentinel_for<S, I>
, returns(last - first)
; otherwise, r Returns the number of increments needed to get fromfirst
tolast
.template<input_or_output_iterator I, sized_sentinel_for<I> S> constexpr iter_difference_t<I> ranges::distance(const I& first, const S& last);-?- Preconditions:
S
andI
modelsized_sentinel_for<S, I>
and either:-? Effects: Returns
(?.1) —
[first, last)
denotes a range, or(?.2) —
[last, first)
denotes a range andS
andI
modelsame_as<S, I>
.(last - first)
;
[2021-05-19 Tim updates wording]
The wording below removes the explicit precondition on the sized_sentinel_for
overload of distance
, relying instead on the semantic requirements of that concept and the "Effects: Equivalent to:" word of power. This also removes the potentially surprising inconsistency that given a non-empty std::vector<int> v
, ranges::distance(v.begin(), v.cend())
is well-defined but ranges::distance(v.cend(), v.begin())
is currently undefined.
[2021-06-23; Reflector poll]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
[2021-10-14 Approved at October 2021 virtual plenary. Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4885.
Modify 24.2 [iterator.synopsis], header <iterator>
synopsis, as indicated:
[…] namespace std { […] // 24.4.4 [range.iter.ops], range iterator operations namespace ranges { […] // 24.4.4.3 [range.iter.op.distance], ranges::distance template<input_or_output_iterator I, sentinel_for<I> S> requires (!sized_sentinel_for<S, I>) constexpr iter_difference_t<I> distance(I first, S last); template<input_or_output_iterator I, sized_sentinel_for<I> S> constexpr iter_difference_t<I> distance(const I& first, const S& last); template<range R> constexpr range_difference_t<R> distance(R&& r); […] } […] }
Modify 24.4.4.3 [range.iter.op.distance] as indicated:
template<input_or_output_iterator I, sentinel_for<I> S> requires (!sized_sentinel_for<S, I>) constexpr iter_difference_t<I> ranges::distance(I first, S last);-1- Preconditions:
-2- Effects: If[first, last)
denotes a range , or[last, first)
denotes a range andS
andI
modelsame_as<S, I> && sized_sentinel_for<S, I>
.S
andI
modelsized_sentinel_for<S, I>
, returns(last - first)
; otherwise, returns the Returns: The number of increments needed to get fromfirst
tolast
.template<input_or_output_iterator I, sized_sentinel_for<I> S> constexpr iter_difference_t<I> ranges::distance(const I& first, const S& last);-?- Effects: Equivalent to
return last - first;
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4