4026. Assignment operators of std::expected should propagate triviality

Section: 22.8.6.4 [expected.object.assign], 22.8.7.4 [expected.void.assign] Status: New Submitter: Jiang An Opened: 2023-12-16 Last modified: 2023-12-22 11:09:04 UTC

Priority: Not Prioritized

View all issues with New status.

Discussion:

Currently, only copy and move constructors of std::expected are required to propagate triviality, while copy and move assignment operators are not. Given that the assignment operators of std::optional and std::variant are already required to propagate triviality, it seems to me that we should also apply such requirements for std::expected.

Such changes are being made in libc++ (llvm/llvm-project#74768). And it may be desired to make the triviality improvement portable.

Proposed resolution:

This wording is relative to N4971.

  1. Modify 22.8.6.4 [expected.object.assign] as indicated:

    constexpr expected& operator=(const expected& rhs);
    

    -2- Effects: […]

    […]

    -4- Remarks: This operator is defined as deleted unless:

    1. […]

    -?- This operator is trivial if:

    1. (?.1) — is_trivially_copy_constructible_v<T> is true, and

    2. (?.2) — is_trivially_copy_assignable_v<T> is true, and

    3. (?.3) — is_trivially_destructible_v<T> is true, and

    4. (?.4) — is_trivially_copy_constructible_v<E> is true, and

    5. (?.5) — is_trivially_copy_assignable_v<E> is true, and

    6. (?.6) — is_trivially_destructible_v<E> is true.

    constexpr expected& operator=(expected&& rhs) noexcept(see below);
    

    -5- Constraints: […]

    […]

    -8- Remarks: The exception specification is equivalent to:

    […]

    -?- This operator is trivial if:

    1. (?.1) — is_trivially_move_constructible_v<T> is true, and

    2. (?.2) — is_trivially_move_assignable_v<T> is true, and

    3. (?.3) — is_trivially_destructible_v<T> is true, and

    4. (?.4) — is_trivially_move_constructible_v<E> is true, and

    5. (?.5) — is_trivially_move_assignable_v<E> is true, and

    6. (?.6) — is_trivially_destructible_v<E> is true.

  2. Modify 22.8.7.4 [expected.void.assign] as indicated:

    constexpr expected& operator=(const expected& rhs);
    

    -1- Effects: […]

    […]

    -3- Remarks: This operator is defined as deleted unless is_copy_assignable_v<E> is true and is_copy_constructible_v<E> is true.

    -?- This operator is trivial if is_trivially_copy_constructible_v<E>, is_trivially_copy_assignable_v<E>, and is_trivially_destructible_v<E> are all true.

    constexpr expected& operator=(expected&& rhs) noexcept(see below);
    

    -4- Effects: […]

    […]

    -6- Remarks: The exception specification is equivalent to is_nothrow_move_constructible_v<E> && is_nothrow_move_assignable_v<E>.

    -7- This operator is defined as deleted unless is_move_constructible_v<E> is true and is_move_assignable_v<E> is true.

    -?- This operator is trivial if is_trivially_move_constructible_v<E>, is_trivially_move_assignable_v<E>, and is_trivially_destructible_v<E> are all true.