This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.
Section: 25.4.4.3 [range.iter.op.distance] Status: WP Submitter: Arthur O'Dwyer Opened: 2022-01-23 Last modified: 2023-02-13
Priority: 2
View all other issues in [range.iter.op.distance].
View all issues with WP 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 subsumes input_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 the I's is constrained and the other isn't.]
Modify 25.2 [iterator.synopsis], header <iterator> synopsis, as indicated:
[…] // 25.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 25.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: [first, last) denotes a range.
-2- Effects: Increments first until last 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 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 from T(&)[N] to T* 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 an if constexpr (is_array_v...) or something like that.]
Modify 25.2 [iterator.synopsis], header <iterator> synopsis, as indicated:
[…] // 25.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 25.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: [first, last) denotes a range.
-2- Effects: Increments first until last 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);