Issue 3918: std::uninitialized_move/_n and guaranteed copy elision (original) (raw)
This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.
3918. std::uninitialized_move/_n and guaranteed copy elision
Section: 26.11.6 [uninitialized.move] Status: WP Submitter: Jiang An Opened: 2023-04-04 Last modified: 2024-11-28
Priority: 3
View all issues with WP 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.
The status quo was mentioned in paper issue #975, but no further process is done since then.
[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
constexpr void* voidify(T& obj) noexcept {
return addressof(obj);
}template
decltype(auto) deref-move(const I& it) {
if constexpr (is_lvalue_reference_v<decltype(*it)>)
return std::move(*it);else
return *it;
} 2. Modify 26.11.6 [uninitialized.move] as indicated:
template<class InputIterator, class NoThrowForwardIterator>
NoThrowForwardIterator uninitialized_move(InputIterator first, InputIterator last,
NoThrowForwardIterator result);-1- Preconditions:
result + [0, (last - first))does not overlap with[first, last).-2- Effects: Equivalent to:
for (; first != last; (void)++result, ++first)
::new (voidify(result))
typename iterator_traits::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:
result + [0, n)does not overlap withfirst + [0, n).-7- Effects: Equivalent to:
for (; n > 0; ++result,(void) ++first, --n)
::new (voidify(result))
typename iterator_traits::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 introducingranges::iter_move into the non-ranges versions wasn't desirable. It was observed that the proposed deref-move has aconst 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
constexpr void* voidify(T& obj) noexcept {
return addressof(obj);
}template
decltype(auto) deref-move(I& it) {
if constexpr (is_lvalue_reference_v<decltype(*it)>)
return std::move(*it);else
return *it;
} 2. Modify 26.11.6 [uninitialized.move] as indicated:
template<class InputIterator, class NoThrowForwardIterator>
NoThrowForwardIterator uninitialized_move(InputIterator first, InputIterator last,
NoThrowForwardIterator result);-1- Preconditions:
result + [0, (last - first))does not overlap with[first, last).-2- Effects: Equivalent to:
for (; first != last; (void)++result, ++first)
::new (voidify(result))
typename iterator_traits::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:
result + [0, n)does not overlap withfirst + [0, n).-7- Effects: Equivalent to:
for (; n > 0; ++result,(void) ++first, --n)
::new (voidify(result))
typename iterator_traits::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.
[Wrocław 2024-11-23; Status changed: Voting → WP.]
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
constexpr void* voidify(T& obj) noexcept {
return addressof(obj);
}template
decltype(auto) deref-move(I& it) {
if constexpr (is_lvalue_reference_v<decltype(*it)>)
return std::move(*it);
else
return *it;
} 2. Modify 26.11.6 [uninitialized.move] as indicated:
template<class InputIterator, class NoThrowForwardIterator>
NoThrowForwardIterator uninitialized_move(InputIterator first, InputIterator last,
NoThrowForwardIterator result);-1- Preconditions:
result + [0, (last - first))does not overlap with[first, last).-2- Effects: Equivalent to:
for (; first != last; (void)++result, ++first)
::new (voidify(result))
typename iterator_traits::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:
result + [0, n)does not overlap withfirst + [0, n).-7- Effects: Equivalent to:
for (; n > 0; ++result,(void) ++first, --n)
::new (voidify(result))
typename iterator_traits::value_type(~~std::move(~~deref-move(first));
return {first, result};