Title
std::uninitialized_move/_n and guaranteed copy elision
Status
voting
Section
[uninitialized.move]
Submitter
Jiang An

Created on 2023-04-04.00:00:00 last changed yesterday

Messages

Date: 2024-06-24.15:47:23

Proposed resolution:

This wording is relative to N4971.

  1. Modify [specialized.algorithms.general] as indicated:

    -3- Some algorithms specified in [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;
      }
    
  2. Modify [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<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: result + [0, n) does not overlap with first + [0, n).

    -7- Effects: Equivalent to:

    for (; n > 0; ++result,(void) ++first, --n)
      ::new (voidify(*result))
        typename iterator_traits<NoThrowForwardIterator>::value_type(std::move(*deref-move(first));
    return {first, result};
    
Date: 2024-06-24.16:06:13

[ 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.

Date: 2024-03-15.00:00:00

[ 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.

This wording is relative to N4971.

  1. Modify [specialized.algorithms.general] as indicated:

    -3- Some algorithms specified in [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;
      }
    
  2. Modify [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<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: result + [0, n) does not overlap with first + [0, n).

    -7- Effects: Equivalent to:

    for (; n > 0; ++result,(void) ++first, --n)
      ::new (voidify(*result))
        typename iterator_traits<NoThrowForwardIterator>::value_type(std::move(*deref-move(first));
    return {first, result};
    
Date: 2023-06-15.00:00:00

[ 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."

This wording is relative to N4944.

  1. Modify [specialized.algorithms.general] as indicated:

    -3- Some algorithms specified in [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;
      }
    
  2. Modify [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<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: result + [0, n) does not overlap with first + [0, n).

    -7- Effects: Equivalent to:

    for (; n > 0; ++result,(void) ++first, --n)
      ::new (voidify(*result))
        typename iterator_traits<NoThrowForwardIterator>::value_type(std::move(*deref-move(first));
    return {first, result};
    
Date: 2023-04-04.00:00:00

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.

History
Date User Action Args
2024-11-19 16:09:07adminsetstatus: ready -> voting
2024-06-26 15:08:03adminsetstatus: open -> ready
2024-06-24 15:47:23adminsetmessages: + msg14197
2024-03-22 01:32:14adminsetmessages: + msg14023
2024-03-22 01:32:14adminsetstatus: lewg -> open
2023-06-01 14:26:42adminsetmessages: + msg13607
2023-06-01 14:26:42adminsetstatus: new -> lewg
2023-04-10 13:40:29adminsetmessages: + msg13515
2023-04-04 00:00:00admincreate