



Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Community
Ask the community for help and clear up your study doubts
Discover the best universities in your country according to Docsity users
Free resources
Download our free guides on studying techniques, anxiety management strategies, and thesis advice from Docsity tutors
The limitations of std::initializer_list and proposes a new class template, initializer_list<T &&>, to allow function parameters that require or leverage strict ownership. The new class template derives from std::initializer_list<T> and assumes ownership of the sequence, allowing for more efficient and safer usage.
Typology: Slides
1 / 6
This page cannot be seen from the preview
Don't miss anything!
Document number: N Date: 2014–10– Reply to: David Krauss (david_work at me dot com)
Often std::initializer_list cannot be used, or it requires a const_cast hack, as it provides read-only access to its sequence. This is a consequence of the sequence potentially being shared with other initializer_list objects, although often it is statically known to be uniquely local. A new initializer list class template is proposed to allow function parameters which may leverage (by overloading) or require strict ownership. Addition of this class does not impinge on the cases where the sequence should be shared. No breaking changes are proposed.
std::initializer_list was designed around 2005 (N1890) to 2007 (N2215), before move semantics matured, around 2009 1. At the time, it was not anticipated that copy semantics would be insufficient or even suboptimal for common value-like classes. There was a 2008 proposal N2801 Initializer lists and move semantics but C++0x was already felt to be slipping at that time, and by 2011 the case had gone cold. It shares many similarities with this proposal. Ownership is very important in modern C++. Users often specify move semantics in the course of an idiomatic pattern, not for the sake of performance. In particular, std::unique_ptr is popular in general use for its simplicity and safety. Although std::initializer_list is often used in constructors to specify the content of a container, it does not own its sequence and cannot give permission to modify, never mind assume ownership of sequence elements. Instead, its interface is based on a generalization where the sequence may be initialized at program startup, and different lists may alias the same immutable sequence. This model is impossible to realize, however, if any list element depends on the context of initialization. Other aspects of object initialization require constructors and destructors to be called, further limiting this optimization. In the majority of uses, ownership exists but it is hidden from the user. When every list element depends on a value computed in its scope, or the list is only used once during the entire program execution and the elements are not of literal type, ownership logically must exist. In these cases, it is safe to remove the const by a const_cast in order to complete a move operation. This is hardly an acceptable practice, and few users will extrapolate the rules correctly. The language needs a facility to safely expose a non-const underlying sequence. (^1) To be fair, move() was first formally presented in 2002 (N1377), and in a mature form, but it remained “in the laboratory” for quite a while. Also, before the appearance of initializer_list, proposals since 2003 had applied array objects directly to the same initialization problem.
A class derived from std::initializer_list
! using initializer_list< T >::begin;! ! constexpr iterator begin() noexcept;! ! using initializer_list< T >::end;! ! constexpr iterator end() noexcept;!
! constexpr initializer_list();! ! initializer_list( initializer_list const & ) = delete;! ! constexpr initializer_list( initializer_list && );!
! ~ initializer_list()! !! noexcept(noexcept(begin()->~T()));! };! A braced-init-list may be passed to an overloaded function, where the corresponding parameter types include both initializer_list<T&&> and initializer_list
void a( std::initializer_list< foo > seq ) ! { generic( seq ); } void a( std::initializer_list< foo && > seq )! ! { generic( seq ); }!
If there is only an initializer_list<T&&> overload, the list is required to own the sequence. void b( std::initializer_list< std::unique_ptr
b({ std::make_unique< int >( 1 ), nullptr }); // OK! When the template parameter of initializer_list is deduced from the content of the list, it the ownership status is not deduced but an && modifier does not interfere with deduction. template< typename T >! void c( std::initializer_list< T > seq ); // #3!
c({ 1, 2, 3 }); // T = int, no write access.! c({ errno }); // Also T = int, no write access.!
template< typename T >! void d( std::initializer_list< T && > seq );!
d({ 1, 2, 3 }); // T = int, write access is guaranteed.!
template< typename T >! void c( std::initializer_list< T && > seq ); // #4 (overload)!
c({ errno }); // Probably calls #4.! The behavior of the auto x = { … } syntax is unchanged; such a variable never exposes write access. auto e = { errno };!
for ( auto && p : { std::make_unique< int >( 5 ) } ) {! ! foo( std::move( p ) ); // Error: p is constant.! }! for ( auto && p! // Explicitly specify ownership:! !!! : std::initializer_list< std::unique_ptr< int > && >! !!!! { std::make_unique< int >( 5 ) } ) {! ! foo( std::move( p ) ); // OK! }! When the user knows there is no benefit to sharing, explicitly naming the template with an rvalue reference type argument guarantees move semantics. This template-id resembles the form of static_cast< T && > used to accomplish a move.
std::map< foo, bar > global_table! ! = std::initializer_list< std::pair< foo, bar > && > {! ! { "lala", { 54 } }, { "barf", { 33 } }! };!
A partial specialization is chosen over a new primary template for the sake of familiarity and simplicity. The && qualifier should be about as easy to remember as adding movable to the template name, and both have similar connotations. Typical users should not need to be aware of the subclass relationship in particular, but it should be intuitive in any case that an owner object may initialize an observer object. This may be more obvious to the user from a similar class name, even if differently-specialized templates are unrelated types in general. Less importantly, much of the current specification is hard-coded to the initializer_list