1. Changelog
-
R0
-
First submission
-
2. Motivation and scope
Please see [P2283R2] ("constexpr for specialized memory algorithms") for the general motivation for these changes.
The wording in [P2283R2] did two things:
-
it added
to most uninitialized memory algorithms;constexpr -
it changed the implementation of them to use
instead of a call tostd :: construct_at
.operator new
With the adoption of [P2747R2] ("
placement new") in the core
language the latter change is no longer needed, and it’s actually
counter-productive as it impedes value elision (for instance, in case the input
iterators to these algorithms return prvalues).
Therefore, this paper proposes an updated wording where we just add
to the algorithms' signatures, as that’s both necessary and
sufficient to achieve the stated goal of being able to use the algorithms from
constant evaluation (and does not pessimize elision).
For the
family of algorithms please see [P3369R0].
3. Impact on the Standard
This proposal is a pure library addition. The necessary changes to core language have already been adopted.
4. Proposed Wording
All the proposed changes are relative to [N4993].
4.1. Feature-testing macro
In [version.syn], modify the value of the
to match the date of adoption of the present proposal.
4.2. Wording
Modify [memory.syn] as shown:
template < class NoThrowForwardIterator > constexpr void uninitialized_value_construct ( NoThrowForwardIterator first , // freestanding NoThrowForwardIterator last ); template < class ExecutionPolicy , class NoThrowForwardIterator > void uninitialized_value_construct ( ExecutionPolicy && exec , // see [algorithms.parallel.overloads] NoThrowForwardIterator first , NoThrowForwardIterator last ); template < class NoThrowForwardIterator , class Size > constexpr NoThrowForwardIterator uninitialized_value_construct_n ( NoThrowForwardIterator first , Size n ); // freestanding template < class ExecutionPolicy , class NoThrowForwardIterator , class Size > NoThrowForwardIterator uninitialized_value_construct_n ( ExecutionPolicy && exec , // see [algorithms.parallel.overloads] NoThrowForwardIterator first , Size n ); namespace ranges { template < nothrow - forward - iterator I , nothrow - sentinel - for < I > S > requires default_initializable < iter_value_t < I >> constexpr I uninitialized_value_construct ( I first , S last ); // freestanding template < nothrow - forward - range R > requires default_initializable < range_value_t < R >> constexpr borrowed_iterator_t < R > uninitialized_value_construct ( R && r ); // freestanding template < nothrow - forward - iterator I > requires default_initializable < iter_value_t < I >> constexpr I uninitialized_value_construct_n ( I first , iter_difference_t < I > n ); // freestanding } template < class InputIterator , class NoThrowForwardIterator > constexpr NoThrowForwardIterator uninitialized_copy ( InputIterator first , // freestanding InputIterator last , NoThrowForwardIterator result ); template < class ExecutionPolicy , class ForwardIterator , class NoThrowForwardIterator > NoThrowForwardIterator uninitialized_copy ( ExecutionPolicy && exec , // see [algorithms.parallel.overloads] ForwardIterator first , ForwardIterator last , NoThrowForwardIterator result ); template < class InputIterator , class Size , class NoThrowForwardIterator > constexpr NoThrowForwardIterator uninitialized_copy_n ( InputIterator first , Size n , // freestanding NoThrowForwardIterator result ); template < class ExecutionPolicy , class ForwardIterator , class Size , class NoThrowForwardIterator > NoThrowForwardIterator uninitialized_copy_n ( ExecutionPolicy && exec , // see [algorithms.parallel.overloads] ForwardIterator first , Size n , NoThrowForwardIterator result ); namespace ranges { template < class I , class O > using uninitialized_copy_result = in_out_result < I , O > ; // freestanding template < input_iterator I , sentinel_for < I > S1 , nothrow - forward - iterator O , nothrow - sentinel - for < O > S2 > requires constructible_from < iter_value_t < O > , iter_reference_t < I >> constexpr uninitialized_copy_result < I , O > uninitialized_copy ( I ifirst , S1 ilast , O ofirst , S2 olast ); // freestanding template < input_range IR , nothrow - forward - range OR > requires constructible_from < range_value_t < OR > , range_reference_t < IR >> constexpr uninitialized_copy_result < borrowed_iterator_t < IR > , borrowed_iterator_t < OR >> uninitialized_copy ( IR && in_range , OR && out_range ); // freestanding template < class I , class O > using uninitialized_copy_n_result = in_out_result < I , O > ; // freestanding template < input_iterator I , nothrow - forward - iterator O , nothrow - sentinel - for < O > S > requires constructible_from < iter_value_t < O > , iter_reference_t < I >> constexpr uninitialized_copy_n_result < I , O > uninitialized_copy_n ( I ifirst , iter_difference_t < I > n , // freestanding O ofirst , S olast ); } template < class InputIterator , class NoThrowForwardIterator > constexpr NoThrowForwardIterator uninitialized_move ( InputIterator first , // freestanding InputIterator last , NoThrowForwardIterator result ); template < class ExecutionPolicy , class ForwardIterator , class NoThrowForwardIterator > NoThrowForwardIterator uninitialized_move ( ExecutionPolicy && exec , // see [algorithms.parallel.overloads] ForwardIterator first , ForwardIterator last , NoThrowForwardIterator result ); template < class InputIterator , class Size , class NoThrowForwardIterator > constexpr pair < InputIterator , NoThrowForwardIterator > uninitialized_move_n ( InputIterator first , Size n , // freestanding NoThrowForwardIterator result ); template < class ExecutionPolicy , class ForwardIterator , class Size , class NoThrowForwardIterator > pair < ForwardIterator , NoThrowForwardIterator > uninitialized_move_n ( ExecutionPolicy && exec , // see [algorithms.parallel.overloads] ForwardIterator first , Size n , NoThrowForwardIterator result ); namespace ranges { template < class I , class O > using uninitialized_move_result = in_out_result < I , O > ; // freestanding template < input_iterator I , sentinel_for < I > S1 , nothrow - forward - iterator O , nothrow - sentinel - for < O > S2 > requires constructible_from < iter_value_t < O > , iter_rvalue_reference_t < I >> constexpr uninitialized_move_result < I , O > uninitialized_move ( I ifirst , S1 ilast , O ofirst , S2 olast ); // freestanding template < input_range IR , nothrow - forward - range OR > requires constructible_from < range_value_t < OR > , range_rvalue_reference_t < IR >> constexpr uninitialized_move_result < borrowed_iterator_t < IR > , borrowed_iterator_t < OR >> uninitialized_move ( IR && in_range , OR && out_range ); // freestanding template < class I , class O > using uninitialized_move_n_result = in_out_result < I , O > ; // freestanding template < input_iterator I , nothrow - forward - iterator O , nothrow - sentinel - for < O > S > requires constructible_from < iter_value_t < O > , iter_rvalue_reference_t < I >> constexpr uninitialized_move_n_result < I , O > uninitialized_move_n ( I ifirst , iter_difference_t < I > n , // freestanding O ofirst , S olast ); } template < class NoThrowForwardIterator , class T > constexpr void uninitialized_fill ( NoThrowForwardIterator first , // freestanding NoThrowForwardIterator last , const T & x ); template < class ExecutionPolicy , class NoThrowForwardIterator , class T > void uninitialized_fill ( ExecutionPolicy && exec , // see [algorithms.parallel.overloads] NoThrowForwardIterator first , NoThrowForwardIterator last , const T & x ); template < class NoThrowForwardIterator , class Size , class T > constexpr NoThrowForwardIterator uninitialized_fill_n ( NoThrowForwardIterator first , Size n , const T & x ); // freestanding template < class ExecutionPolicy , class NoThrowForwardIterator , class Size , class T > NoThrowForwardIterator uninitialized_fill_n ( ExecutionPolicy && exec , // see [algorithms.parallel.overloads] NoThrowForwardIterator first , Size n , const T & x ); namespace ranges { template < nothrow - forward - iterator I , nothrow - sentinel - for < I > S , class T > requires constructible_from < iter_value_t < I > , const T &> constexpr I uninitialized_fill ( I first , S last , const T & x ); // freestanding template < nothrow - forward - range R , class T > requires constructible_from < range_value_t < R > , const T &> constexpr borrowed_iterator_t < R > uninitialized_fill ( R && r , const T & x ); // freestanding template < nothrow - forward - iterator I , class T > requires constructible_from < iter_value_t < I > , const T &> constexpr I uninitialized_fill_n ( I first , iter_difference_t < I > n , const T & x ); // freestanding }
Modify [uninitialized.construct.value] as shown:
template < class NoThrowForwardIterator > constexpr void uninitialized_value_construct ( NoThrowForwardIterator first , // freestanding NoThrowForwardIterator last );
namespace ranges { template < nothrow - forward - iterator I , nothrow - sentinel - for < I > S > requires default_initializable < iter_value_t < I >> constexpr I uninitialized_value_construct ( I first , S last ); // freestanding template < nothrow - forward - range R > requires default_initializable < range_value_t < R >> constexpr borrowed_iterator_t < R > uninitialized_value_construct ( R && r ); // freestanding }
template < class NoThrowForwardIterator , class Size > constexpr NoThrowForwardIterator uninitialized_value_construct_n ( NoThrowForwardIterator first , Size n ); // freestanding
namespace ranges { template < nothrow - forward - iterator I > requires default_initializable < iter_value_t < I >> constexpr I uninitialized_value_construct_n ( I first , iter_difference_t < I > n ); // freestanding }
Modify [uninitialized.copy] as shown:
template < class InputIterator , class NoThrowForwardIterator > constexpr NoThrowForwardIterator uninitialized_copy ( InputIterator first , // freestanding InputIterator last , NoThrowForwardIterator result );
namespace ranges { template < input_iterator I , sentinel_for < I > S1 , nothrow - forward - iterator O , nothrow - sentinel - for < O > S2 > requires constructible_from < iter_value_t < O > , iter_reference_t < I >> constexpr uninitialized_copy_result < I , O > uninitialized_copy ( I ifirst , S1 ilast , O ofirst , S2 olast ); // freestanding template < input_range IR , nothrow - forward - range OR > requires constructible_from < range_value_t < OR > , range_reference_t < IR >> constexpr uninitialized_copy_result < borrowed_iterator_t < IR > , borrowed_iterator_t < OR >> uninitialized_copy ( IR && in_range , OR && out_range ); // freestanding }
template < class InputIterator , class Size , class NoThrowForwardIterator > constexpr NoThrowForwardIterator uninitialized_copy_n ( InputIterator first , Size n , // freestanding NoThrowForwardIterator result );
namespace ranges { template < input_iterator I , nothrow - forward - iterator O , nothrow - sentinel - for < O > S > requires constructible_from < iter_value_t < O > , iter_reference_t < I >> constexpr uninitialized_copy_n_result < I , O > uninitialized_copy_n ( I ifirst , iter_difference_t < I > n , // freestanding O ofirst , S olast ); }
Modify [uninitialized.move] as shown:
template < class InputIterator , class NoThrowForwardIterator > constexpr NoThrowForwardIterator uninitialized_move ( InputIterator first , // freestanding InputIterator last , NoThrowForwardIterator result );
namespace ranges { template < input_iterator I , sentinel_for < I > S1 , nothrow - forward - iterator O , nothrow - sentinel - for < O > S2 > requires constructible_from < iter_value_t < O > , iter_rvalue_reference_t < I >> constexpr uninitialized_move_result < I , O > uninitialized_move ( I ifirst , S1 ilast , O ofirst , S2 olast ); // freestanding template < input_range IR , nothrow - forward - range OR > requires constructible_from < range_value_t < OR > , range_rvalue_reference_t < IR >> constexpr uninitialized_move_result < borrowed_iterator_t < IR > , borrowed_iterator_t < OR >> uninitialized_move ( IR && in_range , OR && out_range ); // freestanding }
template < class InputIterator , class Size , class NoThrowForwardIterator > constexpr pair < InputIterator , NoThrowForwardIterator > uninitialized_move_n ( InputIterator first , Size n , // freestanding NoThrowForwardIterator result );
namespace ranges { template < input_iterator I , nothrow - forward - iterator O , nothrow - sentinel - for < O > S > requires constructible_from < iter_value_t < O > , iter_rvalue_reference_t < I >> constexpr uninitialized_move_n_result < I , O > uninitialized_move_n ( I ifirst , iter_difference_t < I > n , // freestanding O ofirst , S olast ); }
Modify [uninitialized.fill] as shown:
template < class NoThrowForwardIterator , class T > constexpr void uninitialized_fill ( NoThrowForwardIterator first , // freestanding NoThrowForwardIterator last , const T & x );
namespace ranges { template < nothrow - forward - iterator I , nothrow - sentinel - for < I > S , class T > requires constructible_from < iter_value_t < I > , const T &> constexpr I uninitialized_fill ( I first , S last , const T & x ); // freestanding template < nothrow - forward - range R , class T > requires constructible_from < range_value_t < R > , const T &> constexpr borrowed_iterator_t < R > uninitialized_fill ( R && r , const T & x ); // freestanding }
template < class NoThrowForwardIterator , class Size , class T > constexpr NoThrowForwardIterator uninitialized_fill_n ( NoThrowForwardIterator first , Size n , const T & x ); // freestanding
namespace ranges { template < nothrow - forward - iterator I , class T > requires constructible_from < iter_value_t < I > , const T &> constexpr I uninitialized_fill_n ( I first , iter_difference_t < I > n , const T & x ); // freestanding }
5. Acknowledgements
All credits for [P2283R2] go to Michael Schellenberger Costa, whose work has also inspired our [P3369R0].
Thanks to KDAB for supporting this work.
All remaining errors are ours and ours only.