Title
Range access and initializer_list
Status
resolved
Section
[range.access.begin][range.access.end] [range.access.rbegin][range.access.rend]
Submitter
Casey Carter

Created on 2019-08-15.00:00:00 last changed 34 months ago

Messages

Date: 2021-06-23.13:24:25

Proposed resolution:

This wording is relative to N4830.

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

    -1- The name ranges::begin denotes a customization point object ([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 ([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 [range.access.end] as follows:

    -1- The name ranges::end denotes a customization point object ([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 ([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 [range.access.rbegin] as follows:

    -1- The name ranges::rbegin denotes a customization point object ([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 [range.access.rend] as follows:

    -1- The name ranges::rend denotes a customization point object ([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.

    […]

Date: 2021-06-23.00:00:00

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

Date: 2019-10-07.02:21:30

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

Date: 2019-08-23.18:47:28

The specification of ranges::begin in [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 ([initializer.list.syn]) when performing unqualified lookup, since specializations of initializer_list should not satisfy forwarding-range ([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 ([range.access.end]) has a similar poison pill for initializer_list, which should be changed consistently.

Notably ranges::rbegin ([range.access.rbegin]) and ranges::rend ([range.access.rbegin]) as currently specified accept rvalue initializer_list arguments; they find the initializer_list overloads of std::rbegin and std::rend ([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.

History
Date User Action Args
2021-06-23 13:24:25adminsetmessages: + msg11949
2021-06-23 13:24:25adminsetstatus: new -> resolved
2019-10-07 02:21:30adminsetmessages: + msg10672
2019-08-21 01:23:47adminsetmessages: + msg10569
2019-08-15 00:00:00admincreate