std::ranges::distance(a, a+3)
Section: 24.4.4.3 [range.iter.op.distance] Status: C++23 Submitter: Arthur O'Dwyer Opened: 2022-01-23 Last modified: 2023-11-22
Priority: 2
View all other issues in [range.iter.op.distance].
View all issues with C++23 status.
Discussion:
Consider the use of std::ranges::distance(first, last)
on a simple C array.
This works fine with std::distance
, but currently does not work with
std::ranges::distance
.
// godbolt link #include <ranges> #include <cassert> int main() { int a[] = {1, 2, 3}; assert(std::ranges::distance(a, a+3) == 3); assert(std::ranges::distance(a, a) == 0); assert(std::ranges::distance(a+3, a) == -3); }
Before LWG 3392, we had a single iterator-pair overload:
template<input_or_output_iterator I, sentinel_for<I> S> constexpr iter_difference_t<I> distance(I first, S last);
which works fine for C pointers. After LWG 3392, we have two iterator-pair overloads:
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);
and unfortunately the one we want — distance(I first, S last)
—
is no longer viable because [with I=int*
, S=int*
], we have
sized_sentinel_for<S, I>
and so its constraints aren't satisfied.
So we look at the other overload [with I=int[3]
, S=int[3]
], but
unfortunately its constraints aren't satisfied either, because int[3]
is not an input_or_output_iterator
.
[2022-01-30; Reflector poll]
Set priority to 2 after reflector poll.
Previous resolution [SUPERSEDED]:
This wording is relative to N4901.
[Drafting Note: Thanks to Casey Carter. Notice that
sentinel_for<S, I>
already implies and subsumesinput_or_output_iterator<I>
, so that constraint wasn't doing anything; personally I'd prefer to remove it for symmetry (and to save the environment). Otherwise you'll have people asking why one of theI
's is constrained and the other isn't.]
Modify 24.2 [iterator.synopsis], header
<iterator>
synopsis, as indicated:[…] // 24.4.4.3 [range.iter.op.distance], ranges::distance template<classinput_or_output_iteratorI, sentinel_for<I> S> requires (!sized_sentinel_for<S, I>) constexpr iter_difference_t<I> distance(I first, S last); template<classinput_or_output_iteratorI, sized_sentinel_for<decay_t<I>> S> constexpr iter_difference_t<I> distance(const I& first,constS&last); […]Modify 24.4.4.3 [range.iter.op.distance] as indicated:
template<classinput_or_output_iteratorI, 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: Increments[first, last)
denotes a range.first
untillast
is reached and returns the number of increments.template<classinput_or_output_iteratorI, sized_sentinel_for<decay_t<I>> S> constexpr iter_difference_t<I> ranges::distance(const I& first,constS&last);-3- Effects: Equivalent to:
return last - first;
[2022-02-16; Arthur and Casey provide improved wording]
[Kona 2022-11-08; Move to Ready]
[2023-02-13 Approved at February 2023 meeting in Issaquah. Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4901.
[Drafting Note: Arthur thinks it's a bit "cute" of the Effects: element to
static_cast
fromT(&)[N]
toT* const&
in the array case, but it does seem to do the right thing in all cases, and it saves us from having to use anif constexpr (is_array_v...)
or something like that.]
Modify 24.2 [iterator.synopsis], header <iterator>
synopsis, as indicated:
[…] // 24.4.4.3 [range.iter.op.distance], ranges::distance template<classinput_or_output_iteratorI, sentinel_for<I> S> requires (!sized_sentinel_for<S, I>) constexpr iter_difference_t<I> distance(I first, S last); template<classinput_or_output_iteratorI, sized_sentinel_for<decay_t<I>> S> constexpr iter_difference_t<decay_t<I>> distance(constI&& first,constS&last); […]
Modify 24.4.4.3 [range.iter.op.distance] as indicated:
template<classinput_or_output_iteratorI, 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: Increments[first, last)
denotes a range.first
untillast
is reached and returns the number of increments.template<classinput_or_output_iteratorI, sized_sentinel_for<decay_t<I>> S> constexpr iter_difference_t<decay_t<I>> ranges::distance(constI&& first,constS&last);-3- Effects: Equivalent to:
return last - static_cast<const decay_t<I>&>(first);