1. Changelog
- 
     R6 (pre-LWG in Hagenberg): 
- 
     R5 (during Wrocław): - 
       LWG voted 7–1–0 to forward R4 to plenary, but then retracted that vote and asked LEWG to re-confirm the design decision that the generic free functions begin data 
- 
       Add < typeindex > 
- 
       Merge LWG issue resolutions back into "Proposed Wording," per jwakely; LWG approves of these resolutions. 
 
- 
       
- 
     R4 (pre-LWG in Wrocław): 
- 
     R3 (post-LEWG in Tokyo): - 
       Move possible resolutions for LWG3624 and LWG3625 out of the "Proposed Wording" section; LEWG didn’t want to step on LWG’s toes by seeming to bless these issue resolutions 
- 
       For the same reason, remove #include <initializer_list>
 
- 
       
- 
     R2: - 
       Give resolutions for LWG3624 and LWG3625 
- 
       Rebut concerns about breaking code; discuss how a deprecation period for std :: begin ( initializer_list ) 
- 
       Make valarray :: iterator 
- 
       Remove R1’s Historical Background section, as basically distracting at this point 
- 
       Mention [P2613] and [LWG4035] as further precedent for initializer_list . empty () 
- 
       Add feature-test macros __cpp_lib_initializer_list __cpp_lib_valarray 
 
- 
       
- 
     R1: - 
       Split [CWG2825] from this paper; this paper is now library-only 
- 
       Add discussion of . data () . empty () 
- 
       Add wording for ".data+.size cleanup" 
 
- 
       
2. Motivation and proposal
Casey Carter points out that the following program is supported by libstdc++ but not libc++ nor Microsoft (Godbolt):
#include <iterator>#include <valarray>int main () { std :: valarray < int > v = { 1 , 2 , 3 }; std :: begin ( v ); // OK std :: cbegin ( v ); // Error } 
This is because 
Likewise, on all vendors (Godbolt):
#include <iterator>int main () { std :: begin ({ 1 , 2 , 3 }); // OK std :: cbegin ({ 1 , 2 , 3 }); // Error } 
This is because 
Notice that 
Note: Be careful to distinguish the scenario of calling 
We propose to resolve 
2.1. data empty 
   We also propose two more member functions for 
Many places in the library clauses would like to operate on
the contiguous data of an 
constexpr basic_string & append ( const basic_string & str ); 1. Effects: Equivalent to
return append ( str . data (), str . size ()); [...]
constexpr basic_string & append ( initializer_list < charT > il ); 16. Effects: Equivalent to
return append ( il . data (), il . size () il . begin (), il . size () ); 
As for 
By making 
| 
 | 
 | 
| 
 | 
 | 
| 
 | 
 | 
2.2. Availability of begin end 
   The following snippet tries to use 
We propose simply to break the left-hand snippet, and force the programmer to write the right-hand snippet instead. One LEWG reviewer expressed concern that we were breaking valid code without a deprecation period.
| 
 | 
 | 
At first, I was amenable to the idea that the declarations of 
template < class C > constexpr auto begin ( C & c ) -> decltype ( c . begin ()); template < class C > constexpr auto begin ( const C & c ) -> decltype ( c . begin ()); 2. Returns:
.c . begin () x. Remarks: In addition to being available via inclusion of the
header, these function templates are available when< iterator > is included. This availability is deprecated.< initializer_list > 
But library vendors can’t implement that wording! We have 
2.3. Noexcept-specs
In the paper standard for C++23, we find ([iterator.range], [valarray.syn], [initializer.list.syn], [fs.filesystem.syn]):
- 
     begin end initializer_list [ recursive_ ] directory_iterator 
- 
     data empty initializer_list 
- 
     cbegin cend 
- 
     rbegin rend crbegin crend 
- 
     size ssize 
This is explained by two historical quirks:
- 
     The generic cbegin cend cbegin cend initializer_list noexcept 
- 
     The dedicated overloads of rbegin rend reverse_iterator ctr . rbegin () 
We propose to add 
3. Implementation experience
3.1. For the new noexcept-specs
Yes: fully on Microsoft, "mostly" on libstdc++, "partly" on libc++.
Microsoft STL has put noexcept-specs on 
GNU libstdc++ has put noexcept-specs on 
libc++ has put noexcept-specs on 
3.2. For the initializer_list valarray 
   Arthur has implemented § 4 Proposed wording in his fork of libc++ (source), and used it to compile both LLVM/Clang/libc++ and another large C++17 codebase. Naturally, it caused no problems except in this single test from libc++'s own test suite:
#include <initializer_list>// but not <iterator> std :: initializer_list < int > il ; static_assert ( noexcept ( std :: begin ( il ))); 
This test now fails because 
3.3. Tony Table
| 
 | 
 | 
| 
 | 
 | 
| 
 | 
 | 
| 
 | 
 | 
| 
 | 
 | 
| 
 | 
 | 
4. Proposed wording
Note: Vendors should provide 
4.1. Resolve LWG3624
The following changes resolve [LWG3624].
Modify [iterator.synopsis] as follows:
#include <compare>// see [compare.syn] #include <concepts>// see [concepts.syn] #include <initializer_list>// see [initializer.list.syn] 
Modify [any.synop] as follows
(since [any.class] requires both 
#include <initializer_list>// see [initializer.list.syn] #include <typeinfo>// see [typeinfo.syn] namespace std { 
Modify [functional.syn] as follows
(since [func.wrap.move] requires 
#include <initializer_list>// see [initializer.list.syn] #include <typeinfo>// see [typeinfo.syn] namespace std { 
Modify [type.index.synopsis] as follows
(since [type.index.overview] requires 
#include <compare>// see [compare.syn] #include <typeinfo>// see [typeinfo.syn] namespace std { 
Contra LWG3624, do not modify [stacktrace.syn].
It uses 
4.2. [version.syn]
Add two feature-test macros to [version.syn]/2:
#define __cpp_lib_incomplete_container_elements 201505L // also in <forward_list>, <list>, <vector> #define __cpp_lib_initializer_list YYYYMML // also in <initializer_list> #define __cpp_lib_int_pow2 202002L // freestanding, also in <bit> [...] #define __cpp_lib_unwrap_ref 201811L // freestanding, also in <type_traits> #define __cpp_lib_valarray YYYYMML // also in <valarray> #define __cpp_lib_variant 202306L // also in <variant> 
4.3. [valarray.syn]
Modify [valarray.syn] as follows:
[...]template < class T > valarray < T > tan ( const valarray < T >& ); template < class T > valarray < T > tanh ( const valarray < T >& ); template < class T > unspecified1 begin ( valarray < T >& v ); template < class T > unspecified2 begin ( const valarray < T >& v ); template < class T > unspecified1 end ( valarray < T >& v ); template < class T > unspecified2 end ( const valarray < T >& v ); } [...]
3․ Any function returning a
is permitted to return an object of another type, provided all the const member functions ofvalarray < T > other thanvalarray < T > andbegin are also applicable to this type. This return type shall not add more than two levels of template nesting over the most deeply nested argument type.end 4․ Implementations introducing such replacement types shall provide additional functions and operators as follows:
(4.1) for every function taking a
const valarray < T >& other than, identical functions taking the replacement types shall be added;andbegin end 
(4.2) for every function taking two
arguments, identical functions taking every combination ofconst valarray < T >& and replacement types shall be added.const valarray < T >& 5․ In particular, an implementation shall allow a
to be constructed from such replacement types and shall allow assignments and compound assignments of such types tovalarray < T > ,valarray < T > ,slice_array < T > ,gslice_array < T > andmask_array < T > objects.indirect_array < T > [...]
4.4. [template.valarray.overview]
Note: R1 proposed the 
Note: We propose that 
Modify [template.valarray.overview] as follows:
namespace std { template < class T > class valarray { public : using value_type = T ; using iterator = unspecified ; using const_iterator = unspecified ; // [valarray.cons], construct/destroy valarray (); explicit valarray ( size_t ); [...]
// [valarray.range], range access iterator begin (); iterator end (); const_iterator begin () const ; const_iterator end () const ; // [valarray.members], member functions void swap ( valarray & ) noexcept ; size_t size () const ; T sum () const ; T min () const ; T max () const ; valarray shift ( int ) const ; valarray cshift ( int ) const ; valarray apply ( T func ( T )) const ; valarray apply ( T func ( const T & )) const ; void resize ( size_t sz , T c = T ()); }; 
4.5. [valarray.members]
Move the existing section [valarray.range] from its current location to make it a sibling of [valarray.members]; then modify it as follows:
28.6.1028.6.2.xrange access [valarray.range]valarray 1․
In theTheandbegin function templates that follow,end is a type thatunspecified1 type meets the requirements of a mutable Cpp17RandomAccessIterator ([random.access.iterators]) and modelsiterator ([iterator.concept.contiguous])contiguous_iterator , whose. Itsis the template parametervalue_type andT whoseitstype isreference .T & Theis a type thatunspecified2 type meets the requirements of a constant Cpp17RandomAccessIterator and modelsconst_iterator contiguous_iterator , whose. Itsis the template parametervalue_type andT whoseitstype isreference .const T & 2․ The iterators returned by
andbegin for an array are guaranteed to be valid until the member functionend is called for that array or until the lifetime of that array ends, whichever happens first.resize ( size_t , T ) template < class T > unspecified1 begin ( valarray < T >& v ); template < class T > unspecified2 begin ( const valarray < T >& v ); iterator begin (); const_iterator begin () const ; 3․ Returns: An iterator referencing the first value in the array.
template < class T > unspecified1 end ( valarray < T >& v ); template < class T > unspecified2 end ( const valarray < T >& v ); iterator end (); const_iterator end () const ; 4․ Returns: An iterator referencing one past the last value in the array.
28.6.2.8 Member functions [valarray.members]
void swap ( valarray & v ) noexcept ; 1․ Effects:
obtains the value of* this .v obtains the value ofv .* this 2․ Complexity: Constant.
4.6. [support.initlist]
Modify [support.initlist] as follows:
[...]17.10.2 Header
synopsis [initializer.list.syn]< initializer_list > namespace std { template < class E > class initializer_list { public : using value_type = E ; using reference = const E & ; using const_reference = const E & ; using size_type = size_t ; using iterator = const E * ; using const_iterator = const E * ; constexpr initializer_list () noexcept ; constexpr const E * data () const noexcept ; constexpr size_t size () const noexcept ; // number of elements constexpr bool empty () const noexcept ; constexpr const E * begin () const noexcept ; // first element constexpr const E * end () const noexcept ; // one past the last element }; // [support.initlist.range], initializer list range access template < class E > constexpr const E * begin ( initializer_list < E > il ) noexcept ; template < class E > constexpr const E * end ( initializer_list < E > il ) noexcept ; } 1․ An object of type
provides access to an array of objects of typeinitializer_list < E > .const E [Note: A pair of pointers or a pointer plus a length would be obvious representations for
.initializer_list is used to implement initializer lists as specified in [dcl.init.list]. Copying aninitializer_list does not copy the underlying elements. — end note]initializer_list 2․ If an explicit specialization or partial specialization of
is declared, the program is ill-formed.initializer_list 17.10.3 Initializer list constructors [support.initlist.cons]
constexpr initializer_list () noexcept ; 1․ Postconditions:
.size () == 0 17.10.4 Initializer list access [support.initlist.access]
constexpr const E * begin () const noexcept ; 1․ Returns: A pointer to the beginning of the array. If
the values ofsize () == 0 andbegin () are unspecified but they shall be identical.end () constexpr const E * end () const noexcept ; 2․ Returns:
.begin () + size () constexpr const E * data () const noexcept ; x․ Returns:
.begin () constexpr size_t size () const noexcept ; 3․ Returns: The number of elements in the array.
4․ Complexity: Constant
time.constexpr bool empty () const noexcept ; x․ Returns:
.size () == 0 
17.10.5 Initializer list range access [support.initlist.range]template < class E > constexpr const E * begin ( initializer_list < E > il ) noexcept ; 1․ Returns:.il . begin () template < class E > constexpr const E * end ( initializer_list < E > il ) noexcept ; 2․ Returns:.il . end () 
4.7. [iterator.synopsis]
Modify [iterator.synopsis] as follows:
25.2 Header <iterator> synopsis [iterator.synopsis]#include <compare>// see [compare.syn] #include <concepts>// see [concepts.syn] #include <initializer_list>// see [initializer.list.syn] namespace std { [...]
// [iterator.range], range access template < class C > constexpr auto begin ( C & c ) noexcept ( noexcept ( c . begin ())) -> decltype ( c . begin ()); template < class C > constexpr auto begin ( const C & c ) noexcept ( noexcept ( c . begin ())) -> decltype ( c . begin ()); template < class C > constexpr auto end ( C & c ) noexcept ( noexcept ( c . end ())) -> decltype ( c . end ()); template < class C > constexpr auto end ( const C & c ) noexcept ( noexcept ( c . end ())) -> decltype ( c . end ()); template < class T , size_t N > constexpr T * begin ( T ( & array )[ N ]) noexcept ; template < class T , size_t N > constexpr T * end ( T ( & array )[ N ]) noexcept ; template < class C > constexpr auto cbegin ( const C & c ) noexcept ( noexcept ( std :: begin ( c ))) -> decltype ( std :: begin ( c )); template < class C > constexpr auto cend ( const C & c ) noexcept ( noexcept ( std :: end ( c ))) -> decltype ( std :: end ( c )); template < class C > constexpr auto rbegin ( C & c ) noexcept ( noexcept ( c . rbegin ())) -> decltype ( c . rbegin ()); template < class C > constexpr auto rbegin ( const C & c ) noexcept ( noexcept ( c . rbegin ())) -> decltype ( c . rbegin ()); template < class C > constexpr auto rend ( C & c ) noexcept ( noexcept ( c . rend ())) -> decltype ( c . rend ()); template < class C > constexpr auto rend ( const C & c ) noexcept ( noexcept ( c . rend ())) -> decltype ( c . rend ()); template < class T , size_t N > constexpr reverse_iterator < T *> rbegin ( T ( & array )[ N ]) noexcept ; template < class T , size_t N > constexpr reverse_iterator < T *> rend ( T ( & array )[ N ]) noexcept ; template < class E > constexpr reverse_iterator < const E *> rbegin ( initializer_list < E > il ) noexcept ; template < class E > constexpr reverse_iterator < const E *> rend ( initializer_list < E > il ) noexcept ; template < class C > constexpr auto crbegin ( const C & c ) noexcept ( noexcept ( std :: rbegin ( c ))) -> decltype ( std :: rbegin ( c )); template < class C > constexpr auto crend ( const C & c ) noexcept ( noexcept ( std :: rend ( c ))) -> decltype ( std :: rend ( c )); template < class C > constexpr auto size ( const C & c ) noexcept ( noexcept ( c . size ())) -> decltype ( c . size ()); template < class T , size_t N > constexpr size_t size ( const T ( & array )[ N ]) noexcept ; template < class C > constexpr auto ssize ( const C & c ) noexcept ( noexcept ( c . size ())) -> common_type_t < ptrdiff_t , make_signed_t < decltype ( c . size ()) >> ; template < class T , ptrdiff_t N > constexpr ptrdiff_t ssize ( const T ( & array )[ N ]) noexcept ; template < class C > constexpr auto empty ( const C & c ) noexcept ( noexcept ( c . empty ())) -> decltype ( c . empty ()); template < class T , size_t N > constexpr bool empty ( const T ( & array )[ N ]) noexcept ; template < class E > constexpr bool empty ( initializer_list < E > il ) noexcept ; template < class C > constexpr auto data ( C & c ) noexcept ( noexcept ( c . data ())) -> decltype ( c . data ()); template < class C > constexpr auto data ( const C & c ) noexcept ( noexcept ( c . data ())) -> decltype ( c . data ()); template < class T , size_t N > constexpr T * data ( T ( & array )[ N ]) noexcept ; template < class E > constexpr const E * data ( initializer_list < E > il ) noexcept ; } 
4.8. [iterator.range]
LWG approved this addition of 
Modify [iterator.range] as follows:
1. In addition to being available via inclusion of the
header, the function templates in [iterator.range] are available when any of the following headers are included:< iterator > ,< array > ,< deque > ,< flat_map > ,< flat_set > ,< forward_list > ,< inplace_vector > ,< list > ,< map > ,< optional > ,< regex > ,< set > ,< span > ,< stacktrace > ,< string > ,< string_view > ,< unordered_map > ,< unordered_set > , and< valarray > .< vector > template < class C > constexpr auto begin ( C & c ) noexcept ( noexcept ( c . begin ())) -> decltype ( c . begin ()); template < class C > constexpr auto begin ( const C & c ) noexcept ( noexcept ( c . begin ())) -> decltype ( c . begin ()); 2. Returns:
.c . begin () template < class C > constexpr auto end ( C & c ) noexcept ( noexcept ( c . end ())) -> decltype ( c . end ()); template < class C > constexpr auto end ( const C & c ) noexcept ( noexcept ( c . end ())) -> decltype ( c . end ()); 3. Returns:
.c . end () template < class T , size_t N > constexpr T * begin ( T ( & array )[ N ]) noexcept ; 4. Returns:
.array template < class T , size_t N > constexpr T * end ( T ( & array )[ N ]) noexcept ; 5. Returns:
.array + N template < class C > constexpr auto cbegin ( const C & c ) noexcept ( noexcept ( std :: begin ( c ))) -> decltype ( std :: begin ( c )); 6. Returns:
.std :: begin ( c ) template * lt ; class C > constexpr auto cend ( const C & c ) noexcept ( noexcept ( std :: end ( c ))) -> decltype ( std :: end ( c )); 7. Returns:
.std :: end ( c ) template < class C > constexpr auto rbegin ( C & c ) noexcept ( noexcept ( c . rbegin ())) -> decltype ( c . rbegin ()); template < class C > constexpr auto rbegin ( const C & c ) noexcept ( noexcept ( c . rbegin ())) -> decltype ( c . rbegin ()); 8. Returns:
.c . rbegin () template < class C > constexpr auto rend ( C & c ) noexcept ( noexcept ( c . rend ())) -> decltype ( c . rend ()); template < class C > constexpr auto rend ( const C & c ) noexcept ( noexcept ( c . rend ())) -> decltype ( c . rend ()); 9. Returns:
.c . rend () template < class T , size_t N > constexpr reverse_iterator < T *> rbegin ( T ( & array )[ N ]) noexcept ; 10. Returns:
.reverse_iterator < T *> ( array + N ) template < class T , size_t N > constexpr reverse_iterator < T *> rend ( T ( & array )[ N ]) noexcept ; 11. Returns:
.reverse_iterator < T *> ( array ) template < class E > constexpr reverse_iterator < const E *> rbegin ( initializer_list < E > il ) noexcept ; 12. Returns:
.reverse_iterator < const E *> ( il . end ()) template < class E > constexpr reverse_iterator < const E *> rend ( initializer_list < E > il ) noexcept ; 13. Returns:
.reverse_iterator < const E *> ( il . begin ()) template < class C > constexpr auto crbegin ( const C & c ) noexcept ( noexcept ( std :: rbegin ( c ))) -> decltype ( std :: rbegin ( c )); 14. Returns:
.std :: rbegin ( c ) template < class C > constexpr auto crend ( const C & c ) noexcept ( noexcept ( std :: rend ( c ))) -> decltype ( std :: rend ( c )); 15. Returns:
.std :: rend ( c ) template < class C > constexpr auto size ( const C & c ) noexcept ( noexcept ( c . size ())) -> decltype ( c . size ()); 16. Returns:
.c . size () template < class T , size_t N > constexpr size_t size ( const T ( & array )[ N ]) noexcept ; 17. Returns:
.N template < class C > constexpr auto ssize ( const C & c ) noexcept ( noexcept ( c . size ())) -> common_type_t < ptrdiff_t , make_signed_t < decltype ( c . size ()) >> ; 18. Effects: Equivalent to:
return static_cast < common_type_t < ptrdiff_t , make_signed_t < decltype ( c . size ()) >>> ( c . size ()); template < class T , ptrdiff_t N > constexpr ptrdiff_t ssize ( const T ( & array )[ N ]) noexcept ; 19. Returns:
.N template < class C > constexpr auto empty ( const C & c ) noexcept ( noexcept ( c . empty ())) -> decltype ( c . empty ()); 20. Returns:
.c . empty () template < class T , size_t N > constexpr bool empty ( const T ( & array )[ N ]) noexcept ; 21. Returns:
false.template < class E > constexpr bool empty ( initializer_list < E > il ) noexcept ; 
22. Returns:.il . size () == 0 template < class C > constexpr auto data ( C & c ) noexcept ( noexcept ( c . data ())) -> decltype ( c . data ()); template < class C > constexpr auto data ( const C & c ) noexcept ( noexcept ( c . data ())) -> decltype ( c . data ()); 23. Returns:
.c . data () template < class T , size_t N > constexpr T * data ( T ( & array )[ N ]) noexcept ; 24. Returns:
.array template < class E > constexpr const E * data ( initializer_list < E > il ) noexcept ; 
25․ Returns:.il . begin () 
4.9. .data+.size cleanup
4.9.1. [string.cons]
Modify [string.cons] as follows:
constexpr basic_string & operator = ( initializer_list < charT > il ); 36․ Effects: Equivalent to:
return * this = basic_string_view < charT , traits > ( il . begin () il . data () , il . size ()); 
4.9.2. [string.append]
Modify [string.append] as follows:
constexpr basic_string & append ( initializer_list < charT > il ); 16․ Effects: Equivalent to:
return append ( il . begin () il . data () , il . size ()); 
4.9.3. [string.assign]
Modify [string.assign] as follows:
constexpr basic_string & assign ( initializer_list < charT > il ); 12․ Effects: Equivalent to:
return assign ( il . begin () il . data () , il . size ()); 
4.9.4. [string.replace]
Modify [string.replace] as follows:
constexpr basic_string & replace ( const_iterator i1 , const_iterator i2 , initializer_list < charT > il ); 12․ Effects: Equivalent to:
return replace ( i1 , i2 , il . begin () il . data () , il . size ()); 
4.9.5. [span.cons]
Modify [span.cons] as follows:
constexpr explicit ( extent != dynamic_extent ) span ( std :: initializer_list < value_type > il ); 18․ Constraints:
isis_const_v < element_type > true.19․ Preconditions: If
is not equal toextent , thendynamic_extent is equal toil . size () .extent 20․ Effects: Initializes
withdata_ il . begin () andil . data () withsize_ .il . size () 
4.9.6. [valarray.cons]
Modify [valarray.cons] as follows:
valarray ( initializer_list < T > il ); 9․ Effects: Equivalent to
.valarray ( il . begin () il . data () , il . size ())