std::uninitialized_move/_n
and guaranteed copy elisionSection: 26.11.6 [uninitialized.move] Status: Ready Submitter: Jiang An Opened: 2023-04-04 Last modified: 2024-06-26
Priority: 3
View all issues with Ready status.
Discussion:
Currently std::move
is unconditionally used in std::uninitialized_move
and std::uninitialized_move_n
,
which may involve unnecessary move construction if dereferencing the input iterator yields a prvalue.
[2023-06-01; Reflector poll]
Set priority to 3 after reflector poll. Send to LEWG.
"P2283 wants to remove guaranteed elision here."
"Poorly motivated, not clear anybody is using these algos with proxy iterators."
"Consider using iter_move
in the move algos."
Previous resolution [SUPERSEDED]:
This wording is relative to N4944.
Modify 26.11.1 [specialized.algorithms.general] as indicated:
-3- Some algorithms specified in 26.11 [specialized.algorithms] make use of the following exposition-only functions
:voidify
template<class T> constexpr void* voidify(T& obj) noexcept { return addressof(obj); } template<class I> decltype(auto) deref-move(const I& it) { if constexpr (is_lvalue_reference_v<decltype(*it)>) return std::move(*it); else return *it; }Modify 26.11.6 [uninitialized.move] as indicated:
template<class InputIterator, class NoThrowForwardIterator> NoThrowForwardIterator uninitialized_move(InputIterator first, InputIterator last, NoThrowForwardIterator result);[…]-1- Preconditions:
-2- Effects: Equivalent to:result + [0, (last - first))
does not overlap with[first, last)
.for (; first != last; (void)++result, ++first) ::new (voidify(*result)) typename iterator_traits<NoThrowForwardIterator>::value_type(std::move(*deref-move(first)); return result;template<class InputIterator, class Size, class NoThrowForwardIterator> pair<InputIterator, NoThrowForwardIterator> uninitialized_move_n(InputIterator first, Size n, NoThrowForwardIterator result);-6- Preconditions:
-7- Effects: Equivalent to:result + [0, n)
does not overlap withfirst + [0, n)
.for (; n > 0; ++result,(void) ++first, --n) ::new (voidify(*result)) typename iterator_traits<NoThrowForwardIterator>::value_type(std::move(*deref-move(first)); return {first, result};
[2024-03-22; Tokyo: Jonathan updates wording after LEWG review]
LEWG agrees it would be good to do this.
Using iter_move
was discussed, but it was noted that the versions of these
algos in the ranges
namespace already use it and introducing
ranges::iter_move
into the non-ranges versions wasn't desirable.
It was observed that the proposed deref-move has a
const I&
parameter which would be ill-formed for any iterator
with a non-const operator*
member. Suggested removing the const and
recommended LWG to accept the proposed resolution.
Previous resolution [SUPERSEDED]:
This wording is relative to N4971.
Modify 26.11.1 [specialized.algorithms.general] as indicated:
-3- Some algorithms specified in 26.11 [specialized.algorithms] make use of the following exposition-only functions
:voidify
template<class T> constexpr void* voidify(T& obj) noexcept { return addressof(obj); } template<class I> decltype(auto) deref-move(I& it) { if constexpr (is_lvalue_reference_v<decltype(*it)>) return std::move(*it); else return *it; }Modify 26.11.6 [uninitialized.move] as indicated:
template<class InputIterator, class NoThrowForwardIterator> NoThrowForwardIterator uninitialized_move(InputIterator first, InputIterator last, NoThrowForwardIterator result);[…]-1- Preconditions:
-2- Effects: Equivalent to:result + [0, (last - first))
does not overlap with[first, last)
.for (; first != last; (void)++result, ++first) ::new (voidify(*result)) typename iterator_traits<NoThrowForwardIterator>::value_type(std::move(*deref-move(first)); return result;template<class InputIterator, class Size, class NoThrowForwardIterator> pair<InputIterator, NoThrowForwardIterator> uninitialized_move_n(InputIterator first, Size n, NoThrowForwardIterator result);-6- Preconditions:
-7- Effects: Equivalent to:result + [0, n)
does not overlap withfirst + [0, n)
.for (; n > 0; ++result,(void) ++first, --n) ::new (voidify(*result)) typename iterator_traits<NoThrowForwardIterator>::value_type(std::move(*deref-move(first)); return {first, result};
[St. Louis 2024-06-24; revert P/R and move to Ready]
Tim observed that the iterator requirements require all iterators to be const-dereferenceable, so there was no reason to remove the const. Restore the original resolution and move to Ready.
Proposed resolution:
This wording is relative to N4971.
Modify 26.11.1 [specialized.algorithms.general] as indicated:
-3- Some algorithms specified in 26.11 [specialized.algorithms] make use of the following exposition-only functions
:voidify
template<class T> constexpr void* voidify(T& obj) noexcept { return addressof(obj); } template<class I> decltype(auto) deref-move(I& it) { if constexpr (is_lvalue_reference_v<decltype(*it)>) return std::move(*it); else return *it; }
Modify 26.11.6 [uninitialized.move] as indicated:
template<class InputIterator, class NoThrowForwardIterator> NoThrowForwardIterator uninitialized_move(InputIterator first, InputIterator last, NoThrowForwardIterator result);[…]-1- Preconditions:
-2- Effects: Equivalent to:result + [0, (last - first))
does not overlap with[first, last)
.for (; first != last; (void)++result, ++first) ::new (voidify(*result)) typename iterator_traits<NoThrowForwardIterator>::value_type(std::move(*deref-move(first)); return result;template<class InputIterator, class Size, class NoThrowForwardIterator> pair<InputIterator, NoThrowForwardIterator> uninitialized_move_n(InputIterator first, Size n, NoThrowForwardIterator result);-6- Preconditions:
-7- Effects: Equivalent to:result + [0, n)
does not overlap withfirst + [0, n)
.for (; n > 0; ++result,(void) ++first, --n) ::new (voidify(*result)) typename iterator_traits<NoThrowForwardIterator>::value_type(std::move(*deref-move(first)); return {first, result};