Doc. no.: | P0602R2 |
---|---|

Date: | 2018-03-12 |

Audience: | Library Working Group |

Reply-to: | Zhihao Yuan <zy at miator dot net> |

Making `variant`

and `optional`

conditionally trivially copy constructible, trivially move constructible, trivially copy assignable, and trivially move assignable significantly improves codegen and performance when they are nested or being put into containers.

Given the spirit of the resolution of LWG 2900 and feedback from implementers, this paper proposes a full-fledged approach shipped in libc++, MS STL, and libstd++ that is to make the four operations individually trivial if all alternatives have the corresponding operations being trivial.

This change comes with a (good) potential ABI breakage in user’s code – a small enough trivially copyable `optional`

& `variant`

took or returned by a function can be passed via register after applying the change, therefore we propose to accept this change as a defect report against C++17.

This wording is relative to N4727.

Modify 23.6.3.1 [optional.ctor] as follows:

`constexpr optional(const optional& rhs);`

[…]

Remarks:This constructor shall be defined as deleted unless`is_copy_constructible_v<T>`

is`true`

. If`is_trivially_copy_constructible_v<T>`

is`true`

, this constructor shall be a`constexpr`

trivial constructor.

`constexpr optional(optional&& rhs) noexcept(see below );`

[…]

Remarks:The expression inside`noexcept`

is equivalent to`is_nothrow_move_constructible_v<T>`

. This constructor shall not participate in overload resolution unless`is_move_constructible_v<T>`

is`true`

. If`is_trivially_move_constructible_v<T>`

is`true`

, this constructor shall be a`constexpr`

trivial constructor.

Modify 23.6.3.3 [optional.assign] as follows:

`optional<T>& operator=(const optional& rhs);`

[…]

Remarks:If any exception is thrown, […]. This operator shall be defined as deleted unless`is_copy_constructible_v<T>`

is`true`

and`is_copy_assignable_v<T>`

is`true`

. Ifis_trivially_copy_constructible_v<T> && is_trivially_copy_assignable_v<T> && is_trivially_destructible_v<T>is`true`

, this assignment operator shall be trivial.

`optional<T>& operator=(optional&& rhs) noexcept(see below );`

[…]

Remarks:The expression inside`noexcept`

is equivalent to: […] This operator shall not participate in overload resolution unless`is_move_constructible_v<T>`

is`true`

and`is_move_assignable_v<T>`

is`true`

. Ifis_trivially_move_constructible_v<T> && is_trivially_move_assignable_v<T> && is_trivially_destructible_v<T>is`true`

, this assignment operator shall be trivial.

Modify 23.7.3.1 [variant.ctor] as follows:

`variant(const variant& w);`

[…]

Remarks:This constructor shall be defined as deleted unless`is_copy_constructible_v<T`

_{i}`>`

is`true`

for alli. Ifis_trivially_copy_constructible_v<T_{i}>is`true`

for alli, this constructor shall be trivial.

`variant(variant&& w) noexcept(see below );`

[…]

Remarks:The expression inside`noexcept`

is equivalent to […]. This function shall not participate in overload resolution unless`is_move_constructible_v<T`

_{i}`>`

is`true`

for alli. Ifis_trivially_move_constructible_v<T_{i}>is`true`

for alli, this constructor shall be trivial.

Modify 23.7.3.3 [variant.assign] as follows:

`variant& operator=(const variant& rhs);`

[…]

Remarks:This operator shall be defined as deleted unless`is_copy_constructible_v<T`

_{i}`> &&`

`is_copy_assignable_v<T`

_{i}`>`

is`true`

for alli. Ifis_trivially_copy_constructible_v<T_{i}> && is_trivially_copy_assignable_v<T_{i}> && is_trivially_destructible_v<T_{i}>is`true`

for alli, this assignment operator shall be trivial. […]

`variant& operator=(variant&& rhs) noexcept(see below );`

[…]

Remarks:This function shall not participate in overload resolution unless`is_move_constructible_v<T`

_{i}`> &&`

`is_move_assignable_v<T`

_{i}`>`

is`true`

for alli. Ifis_trivially_move_constructible_v<T_{i}> && is_trivially_move_assignable_v<T_{i}> && is_trivially_destructible_v<T_{i}>is`true`

for alli, this assignment operator shall be trivial. The expression inside`noexcept`

is equivalent to: […]

Thanks Casey Carter for evolving the implementations and explaining the results.