Title
Move assignment for `indirect` unnecessarily requires copy construction
Status
new
Section
[indirect.asgn]
Submitter
Jonathan Wakely

Created on 2025-05-01.00:00:00 last changed 7 days ago

Messages

Date: 2025-05-01.18:05:10

Proposed resolution:

This wording is relative to N5008.

  1. Modify [indirect.asgn] as indicated:

    
    constexpr indirect& operator=(indirect&& other)
      noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
               allocator_traits<Allocator>::is_always_equal::value);
    

    -5- Mandates: If allocator_traits<Allocator>::propagate_on_container_move_assignment::value is `false` and allocator_traits<Allocator>::is_always_equal::value is `false`, is_copymove_constructible_t<T> is `true`.

    -6- Effects: If `addressof(other) == this` is `true`, there are no effects. Otherwise:

    1. (6.1) — The allocator needs updating if allocator_traits<Allocator>::propagate_on_container_move_assignment::value is `true`.
    2. (6.2) — If `other` is valueless, `*this` becomes valueless and the owned object in `*this`, if any, is destroyed using allocator_traits<Allocator>::destroy and then the storage is deallocated.
    3. (6.3) — Otherwise, if the allocator needs updating or if alloc == other.alloc is `true`, swaps the owned objects in `*this` and `other`; the owned object in `other`, if any, is then destroyed using allocator_traits<Allocator>::destroy and then the storage is deallocated `*this` takes ownership of the owned object of `other`.
    4. (6.4) — Otherwise, constructs a new owned object with the owned object of `other` as the argument as an rvalue, using either the allocator in `*this` or the allocator in `other` if the allocator needs updating.
    5. (6.5) — The previously owned object in `*this`, if any, is destroyed using allocator_traits<Allocator>::destroy and then the storage is deallocated.
    6. (6.6) — If the allocator needs updating, the allocator in `*this` is replaced with a copy of the allocator in `other`.

    -7- Postcondition: `other` is valueless.

Date: 2025-05-01.20:35:25

The move assignment operator for `indirect` says:

Mandates: is_copy_constructible_t<T> is `true`.
However, the only way it ever construct an object is:
constructs a new owned object with the owned object of `other` as the argument as an rvalue
and that only ever happens when alloc == other.alloc is false.

It seems like we should require `is_move_constructible_v` instead, and only if the allocator traits mean we need to construct an object. (Technically move-constructible might not be correct, because the allocator's `construct` member might use a different constructor).

Additionally, the noexcept-specifier for the move assignment doesn't match the effects. The noexcept-specifier says it can't throw if POCMA is true, but nothing in the effects says that ownership can be transferred in that case; we only do a non-throwing transfer when the allocators are equal. I think we should transfer ownership when POCMA is true, which would make the noexcept-specifier correct.

History
Date User Action Args
2025-05-01 18:05:10adminsetmessages: + msg14740
2025-05-01 00:00:00admincreate