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.

### 3453. Generic code cannot call `ranges::advance(i, s)`

Section: 23.4.4.2 [range.iter.op.advance], 23.3.4.7 [iterator.concept.sentinel] Status: WP Submitter: Casey Carter Opened: 2020-06-18 Last modified: 2020-11-09

Priority: 2

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

View all issues with WP status.

Discussion:

The specification of the iterator & sentinel overload of `ranges::advance` in 23.4.4.2 [range.iter.op.advance] reads:

>     template<input_or_output_iterator I, sentinel_for<I> S>
>       constexpr void ranges::advance(I& i, S bound);

> -3- Preconditions: `[i, bound)` denotes a range.

> -4- Effects:

> 1. (4.1) — If `I` and `S` model `assignable_from<I&, S>`, equivalent to `i = std::move(bound)`.

> 2. (4.2) — […]

The assignment optimization in bullet 4.1 is just fine for callers with concrete types who can decide whether or not to call `advance` depending on the semantics of the assignment performed. However, since this assignment operation isn't part of the `input_or_output_iterator` or `sentinel_for` requirements its semantics are unknown for arbitrary types. Effectively, generic code is forbidden to call this overload of `advance` when `assignable_from<I&, S>` is satisfied and non-generic code must tread lightly. This seems to make the library dangerously unusable. We can correct this problem by either:

1. Making the assignment operation in question an optional part of the `sentinel_for` concept with well-defined semantics. This concept change should be relatively safe given that `assignable_from<I&, S>` requires `common_reference_with<const I&, const S&>`, which is very rarely satisfied inadvertently.

2. Requiring instead `same_as<I, S>` to trigger the assignment optimization in bullet 4.1 above. `S` is `semiregular`, so `i = std::move(s)` is certainly well-formed (and has well-defined semantics thanks to `semiregular`) when `I` and `S` are the same type. The optimization will not apply in as many cases, but we don't need to make a scary concept change, either.

[2020-06-26; Reflector prioritization]

Set priority to 2 after reflector discussions.

[2020-08-21; Issue processing telecon: Option A is Tentatively Ready]

Previous resolution [SUPERSEDED]:

> This wording is relative to N4861.

> Wording for both Option A and Option B are provided.

> Option A:

> 1. Modify 23.3.4.7 [iterator.concept.sentinel] as indicated:

>    >     template<class S, class I>
>    >       concept sentinel_for =
>    >         semiregular<S> &&
>    >         input_or_output_iterator<I> &&
>    >         weakly-equality-comparable-with<S, I>; // See 18.5.3 [concept.equalitycomparable]

>    > > -2- Let `s` and `i` be values of type `S` and `I` such that `[i, s)` denotes a range. Types `S` and `I` model `sentinel_for<S, I>` only if

>    > > 1. (2.1) — `i == s` is well-defined.

>    > > 2. (2.2) — If `bool(i != s)` then `i` is dereferenceable and `[++i, s)` denotes a range.

>    > > 3. (2.?) — `assignable_from<I&, S>` is either modeled or not satisfied.

> Option B:

> 1. Modify 23.4.4.2 [range.iter.op.advance] as indicated:

>    >     template<input_or_output_iterator I, sentinel_for<I> S>
>    >       constexpr void ranges::advance(I& i, S bound);

>    > > -3- Preconditions: `[i, bound)` denotes a range.

>    > > -4- Effects:

>    > > 1. (4.1) — If `I` and `S` model `assignable_from<I&, S>same_as<I, S>`, equivalent to `i = std::move(bound)`.

>    > > 2. (4.2) — […]

[2020-11-09 Approved In November virtual meeting. Status changed: Tentatively Ready → WP.]

Proposed resolution:

This wording is relative to N4861.

1. Modify 23.3.4.7 [iterator.concept.sentinel] as indicated:

   >     template<class S, class I>
   >       concept sentinel_for =
   >         semiregular<S> &&
   >         input_or_output_iterator<I> &&
   >         weakly-equality-comparable-with<S, I>; // See 18.5.3 [concept.equalitycomparable]

   > > -2- Let `s` and `i` be values of type `S` and `I` such that `[i, s)` denotes a range. Types `S` and `I` model `sentinel_for<S, I>` only if

   > > 1. (2.1) — `i == s` is well-defined.

   > > 2. (2.2) — If `bool(i != s)` then `i` is dereferenceable and `[++i, s)` denotes a range.

   > > 3. (2.?) — `assignable_from<I&, S>` is either modeled or not satisfied.