Issue 3664: LWG 3392 broke std::ranges::distance(a, a+3) (original) (raw)
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.
3664. LWG 3392 broke 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 withstd::ranges::distance.
// godbolt link #include #include
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(i), we had a single iterator-pair overload:
template<input_or_output_iterator I, sentinel_for S> constexpr iter_difference_t distance(I first, S last);
which works fine for C pointers. After LWG 3392(i), we have two iterator-pair overloads:
template<input_or_output_iterator I, sentinel_for S> requires (!sized_sentinel_for<S, I>) constexpr iter_difference_t distance(I first, S last);
template<input_or_output_iterator I, sized_sentinel_for S> constexpr iter_difference_t 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 havesized_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 S>
requires (!sized_sentinel_for<S, I>)
constexpr iter_difference_t distance(I first, S last);
template<classinput_or_output_iteratorI, sized_sentinel_for<decay_t> S>
constexpr iter_difference_t distance(const I& first,constS&last);
[…]- Modify 24.4.4.3 [range.iter.op.distance] as indicated:
template<class
input_or_output_iteratorI, sentinel_for S>
requires (!sized_sentinel_for<S, I>)
constexpr iter_difference_t ranges::distance(I first, S last);-1- Preconditions:
[first, last)denotes a range.-2- Effects: Increments
firstuntillastis reached and returns the number of increments.template<class
input_or_output_iteratorI, sized_sentinel_for<decay_t> S>
constexpr iter_difference_t 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_castfromT(&)[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 S>
requires (!sized_sentinel_for<S, I>)
constexpr iter_difference_t distance(I first, S last);
template<classinput_or_output_iteratorI, sized_sentinel_for<decay_t> S>
constexpr iter_difference_t<decay_t> distance(constI&& first,constS&last);
[…] - Modify 24.4.4.3 [range.iter.op.distance] as indicated:
template<class
input_or_output_iteratorI, sentinel_for S>
requires (!sized_sentinel_for<S, I>)
constexpr iter_difference_t ranges::distance(I first, S last);-1- Preconditions:
[first, last)denotes a range.-2- Effects: Increments
firstuntillastis reached and returns the number of increments.template<class
input_or_output_iteratorI, sized_sentinel_for<decay_t> S>
constexpr iter_difference_t<decay_t> ranges::distance(constI&& first,constS&last);-3- Effects: Equivalent to:
return last - static_cast<const decay_t<I>&>(first);