This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

### 3664. LWG 3392 broke `std::ranges::distance(a, a+3)`

Section: 23.4.4.3 [range.iter.op.distance] Status: New Submitter: Arthur O'Dwyer Opened: 2022-01-23 Last modified: 2022-02-18

Priority: 2

View all other issues in [range.iter.op.distance].

View all issues with New 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.]

> 1. Modify 23.2 [iterator.synopsis], header `<iterator>` synopsis, as indicated:

>    >     […]
>    >     // 23.4.4.3 [range.iter.op.distance], ranges::distance
>    >     template<classinput_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<classinput_or_output_iterator I, sized_sentinel_for<decay_t<I>> S>
>    >       constexpr iter_difference_t<I> distance(const I& first, const S& last);
>    >     […]

> 2. Modify 23.4.4.3 [range.iter.op.distance] as indicated:

>    >     template<classinput_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: `[first, last)` denotes a range.

>    > > -2- Effects: Increments `first` until `last` is reached and returns the number of increments.

>    >     template<classinput_or_output_iterator I, sized_sentinel_for<decay_t<I>> S>
>    >       constexpr iter_difference_t<I> ranges::distance(const I& first, const S& last);

>    > > -3- Effects: Equivalent to: `return last - first;`

[2022-02-16; Arthur and Casey provide improved wording]

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.]

1. Modify 23.2 [iterator.synopsis], header `<iterator>` synopsis, as indicated:

   >     […]
   >     // 23.4.4.3 [range.iter.op.distance], ranges::distance
   >     template<classinput_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<classinput_or_output_iterator I, sized_sentinel_for<decay_t<I>> S>
   >       constexpr iter_difference_t<decay_t<I>> distance(const I&& first, const S& last);
   >     […]

2. Modify 23.4.4.3 [range.iter.op.distance] as indicated:

   >     template<classinput_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: `[first, last)` denotes a range.

   > > -2- Effects: Increments `first` until `last` is reached and returns the number of increments.

   >     template<classinput_or_output_iterator I, sized_sentinel_for<decay_t<I>> S>
   >       constexpr iter_difference_t<decay_t<I>> ranges::distance(const I&& first, const S& last);

   > > -3- Effects: Equivalent to: `return last - static_cast<const decay_t<I>&>(first);`