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

### 3258. Range access and `initializer_list`

Section: 26.3.2 [range.access.begin], 26.3.3 [range.access.end], 26.3.6 [range.access.rbegin], 26.3.7 [range.access.rend] Status: Resolved Submitter: Casey Carter Opened: 2019-08-15 Last modified: 2021-06-23

Priority: 3

View all issues with Resolved status.

Discussion:

The specification of `ranges::begin` in 26.3.2 [range.access.begin] includes a "poison pill" overload:

>     template<class T> void begin(initializer_list<T>&&) = delete;

which exists to create an ambiguity with the non-member `initializer_list` overload of `begin` in namespace `std` (17.11.2 [initializer.list.syn]) when performing unqualified lookup, since specializations of `initializer_list` should not satisfy `forwarding-range` (26.4.2 [range.range]). The design intent is that `const` specializations of `initializer_list` should also not satisfy `forwarding-range`, although they are rare enough beasts that they were overlooked when this wording is written.

`ranges::end` (26.3.3 [range.access.end]) has a similar poison pill for `initializer_list`, which should be changed consistently.

Notably `ranges::rbegin` (26.3.6 [range.access.rbegin]) and `ranges::rend` (26.3.6 [range.access.rbegin]) as currently specified accept rvalue `initializer_list` arguments; they find the `initializer_list` overloads of `std::rbegin` and `std::rend` (25.7 [iterator.range]) via ADL. While I can't put my finger on anything in particular that's broken by this behavior, it seems wise to make `rbegin` and `rend` consistent with `begin` and `end` for `initializer_list` until and unless we discover a reason to do otherwise.

[2019-10 Priority set to 3 after reflector discussion]

[2021-06-23 Resolved by adoption of P2091R0 in Prague. Status changed: New → Resolved.]

Proposed resolution:

This wording is relative to N4830.

1. Modify 26.3.2 [range.access.begin] as follows:

   > -1- The name `ranges::begin` denotes a customization point object (16.3.3.3.6 [customization.point.object]). The expression `ranges::begin(E)` for some subexpression `E` is expression-equivalent to:

   > (1.1) — `E + 0` if `E` is an lvalue of array type (6.8.4 [basic.compound]).

   > (1.2) — Otherwise, if `E` is an lvalue, `decay-copy(E.begin())` if it is a valid expression and its type `I` models `input_or_output_iterator`.

   > (1.3) — Otherwise, `decay-copy(begin(E))` if it is a valid expression and its type `I` models `input_or_output_iterator` with overload resolution performed in a context that includes the declarations:

   > >     template<class T> void begin(T&&) = delete;
   > >     template<class T> void begin(initializer_list<T>&&) = delete;

   > and does not include a declaration of `ranges::begin`.

   > […]

2. Modify 26.3.3 [range.access.end] as follows:

   > -1- The name `ranges::end` denotes a customization point object (16.3.3.3.6 [customization.point.object]). The expression `ranges::end(E)` for some subexpression `E` is expression-equivalent to:

   > (1.1) — `E + extent_v<T>` if `E` is an lvalue of array type (6.8.4 [basic.compound]) `T`.

   > (1.2) — Otherwise, if `E` is an lvalue, `decay-copy(E.end())` if it is a valid expression and its type `S` models `sentinel_for<decltype(ranges::begin(E))>`

   > (1.3) — Otherwise, `decay-copy(end(E))` if it is a valid expression and its type `S` models `sentinel_for<decltype(ranges::begin(E))>` with overload resolution performed in a context that includes the declarations:

   > >     template<class T> void end(T&&) = delete;
   > >     template<class T> void end(initializer_list<T>&&) = delete;

   > and does not include a declaration of `ranges::end`.

   > […]

3. Modify 26.3.6 [range.access.rbegin] as follows:

   > -1- The name `ranges::rbegin` denotes a customization point object (16.3.3.3.6 [customization.point.object]). The expression `ranges::rbegin(E)` for some subexpression `E` is expression-equivalent to:

   > (1.1) — If `E` is an lvalue, `decay-copy(E.rbegin())` if it is a valid expression and its type `I` models `input_or_output_iterator`.

   > (1.2) — Otherwise, `decay-copy(rbegin(E))` if it is a valid expression and its type `I` models `input_or_output_iterator` with overload resolution performed in a context that includes the declarations:

   > >     template<class T> void rbegin(T&&) = delete;
   > >     template<class T> void rbegin(initializer_list<T>) = delete;

   > and does not include a declaration of `ranges::rbegin`.

   > […]

4. Modify 26.3.7 [range.access.rend] as follows:

   > -1- The name `ranges::rend` denotes a customization point object (16.3.3.3.6 [customization.point.object]). The expression `ranges::rend(E)` for some subexpression `E` is expression-equivalent to:

   > (1.1) — If `E` is an lvalue, `decay-copy(E.rend())` if it is a valid expression and its type `S` models

   > >     sentinel_for<decltype(ranges::rbegin(E))>

   > (1.2) — Otherwise, `decay-copy(rend(E))` if it is a valid expression and its type `S` models

   > >     sentinel_for<decltype(ranges::rbegin(E))>

   > with overload resolution performed in a context that includes the declarations:

   > >     template<class T> void rend(T&&) = delete;
   > >     template<class T> void rend(initializer_list<T>) = delete;

   > and does not include a declaration of `ranges::rend`.

   > […]