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
first
untillast
is 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_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 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
first
untillast
is 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);