2839. Self-move-assignment of library types, again

Section: 20.5.5.15 [lib.types.movedfrom], 20.5.4.9 [res.on.arguments], 26.2.1 [container.requirements.general] Status: New Submitter: Tim Song Opened: 2016-12-09 Last modified: 2017-02-02

Priority: 2

View all issues with New status.

Discussion:

LWG 2468's resolution added to MoveAssignable the requirement to tolerate self-move-assignment, but that does nothing for library types that aren't explicitly specified to meet MoveAssignable other than make those types not meet MoveAssignable any longer.

To realize the intent here, we need to carve out an exception to 20.5.4.9 [res.on.arguments]'s restriction for move assignment operators and specify that self-move-assignment results in valid but unspecified state unless otherwise specified. The proposed wording below adds that to 20.5.5.15 [lib.types.movedfrom] since it seems to fit well with the theme of the current paragraph in that section.

In addition, to address the issue with 26.2.1 [container.requirements.general] noted in LWG 2468's discussion, the requirement tables in that subclause will need to be edited in a way similar to LWG 2468.

[2017-01-27 Telecon]

Priority 2

Proposed resolution:

This wording is relative to N4618.

  1. Add a new paragraph at the end of 20.5.5.15 [lib.types.movedfrom]:

    -1- Objects of types defined in the C++ standard library may be moved from (12.8). Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.

    -?- An object of a type defined in the C++ standard library may be move-assigned (15.8.2 [class.copy.assign]) to itself. Such an assignment places the object in a valid but unspecified state unless otherwise specified.

  2. Add a note at the end of 20.5.4.9 [res.on.arguments]/1, bullet 3, as indicated:

    -1- Each of the following applies to all arguments to functions defined in the C++ standard library, unless explicitly stated otherwise.

    1. (1.1) — […]

    2. (1.2) — […]

    3. (1.3) — If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to this argument. [Note: If the parameter is a generic parameter of the form T&& and an lvalue of type A is bound, the argument binds to an lvalue reference (14.8.2.1) and thus is not covered by the previous sentence. — end note] [Note: If a program casts an lvalue to an xvalue while passing that lvalue to a library function (e.g. by calling the function with the argument std::move(x)), the program is effectively asking that function to treat that lvalue as a temporary. The implementation is free to optimize away aliasing checks which might be needed if the argument was an lvalue. — end note] [Note: This does not apply to the argument passed to a move assignment operator (20.5.5.15 [lib.types.movedfrom]). — end note]

  3. Edit Table 83 "Container requirements" in 26.2.1 [container.requirements.general] as indicated:

    Table 83 — Container requirements
    Expression Return type Operational
    semantics
    Assertion/note
    pre-/post-condition
    Complexity
    a = rv T& All existing elements of a
    are either move
    assigned to or
    destroyed
    post: If a and rv do not refer to the same object,
    a shall be equal to the value that
    rv had before this assignment
    linear
  4. Edit Table 86 "Allocator-aware container requirements" in 26.2.1 [container.requirements.general] as indicated:

    Table 86 — Allocator-aware container requirements
    Expression Return type Assertion/note
    pre-/post-condition
    Complexity
    a = rv T& Requires: If allocator_traits<allocator_type
    >::propagate_on_container_move_assignment::value

    is false, T is MoveInsertable
    into X and MoveAssignable.
    All existing elements of a are either
    move assigned to or destroyed.
    post: If a and rv do not refer
    to the same object,
    a shall be equal
    to the value that rv had before this assignment
    linear