This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++17 status.

### 2900. The copy and move constructors of `optional` are not `constexpr`

Section: 20.6.3 [optional.optional] Status: C++17 Submitter: United States Opened: 2017-02-03 Last modified: 2020-09-06

Priority: Not Prioritized

View all other issues in [optional.optional].

View all issues with C++17 status.

Discussion:

Addresses US 111

The copy and move constructors of `optional` are not `constexpr`. However, the constructors taking a `const T&` or `T&&` are `constexpr`, and there is a precedent for having a `constexpr` copy constructor in 26.4.3 [complex]. The defaulted copy and move constructors of `pair` and `tuple` are also conditionally `constexpr` (see 20.4.2 [pairs.pair] p2 and 20.5.2.1 [tuple.cnstr] p2).

A strong motivating use-case is `constexpr` functions returning optional values. This issue was discovered while working on a library making heavy use of such.

Proposed change: Add constexpr to:

>     optional(const optional &);
>     optional(optional &&) noexcept(see below);

[2017-02-23, Casey comments and suggests wording]

This issue corresponds to NB comment US 111, which requests that the move and copy constructors of `std::optional` be declared `constexpr`. The PR simply suggests adding the `constexpr` specifier to the declarations of the constructors. The PR fails to specify the most important thing — and this has been a failing of Library in general — under what conditions is the thing that we've declared constexpr actually expected to be usable in constant expression context? (I think the proper standardese here is "under what conditions is the full expression of an initialization that would invoke these constructors a constant subexpression?")

It is, I believe, well-known that conforming implementations of `optional<T>` must store a `T` in a union to provide constexpr constructors that either do [`optional(T const&)`] or do not [`optional()`] initialize the contained `T`. A general implementation of `optional`'s copy/move constructors must statically choose which union member, if any, to activate in each constructor. Since there is no way to change the active member of a union in a constant expression, and a constructor must statically choose a union member to activate (i.e., without being affected by the runtime state of the copy/move constructor's argument) it's not possible to implement a general constexpr copy/move constructor for `optional`.

It is, however, possible to copy/move construct a trivially copy constructible/trivially move constructible union in `constexpr` context, which effectively copies the union's object representation, resulting in a union whose active member is the same as the source union's. Indeed, at least two major implementations of `optional` (MSVC and libc++) already provide that behavior as a conforming optimization when `T` is a trivially copyable type. If we are to declare `optional<T>`'s copy and move constructors `constexpr`, we should additionally specify that those constructors are only required to have the "constexpr mojo" when `T` is trivially copyable. (Note that I suggest "trivially copyable" here rather than "trivially copy constructible or trivially move constructible" since the simpler requirement is simpler to implement, and I don't believe the more complicated requirement provides any additional benefit: I've never seen a trivially copy constructible or trivially move constructible type outside of a test suite that was not also trivially copyable.)

Previous resolution [SUPERSEDED]:

> This wording is relative to N4618.

> 1. Edit 20.6.3 [optional.optional] as indicated:

>    >     	constexpr optional(const optional &);
>    >     	constexpr optional(optional &&) noexcept(see below);

> 2. Edit 20.6.3.2 [optional.ctor] paragraph as indicated:

>    >     	constexpr optional(const optional &);

>    and

>    >     	constexpr optional(optional &&) noexcept(see below);

[2017-02-23, Marshall comments]

This is related to LWG 2745.

[2017-02-28, Kona, Casey comments and improves wording]

Amended PR per LWG discussion in Kona: replace the "is trivially copyable" requirement with the more specific `is_trivially_copy/move_constructible<T>`. LWG was concerned that `tuple` is a counter-example to the assumption that all three traits are equivalent for real-world types.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4640.

> 1. Change the synopsis of class template `optional` in 20.6.3 [optional.optional] as follows:

>    >     […]
>    >     // 20.6.3.1, constructors
>    >     constexpr optional() noexcept;
>    >     constexpr optional(nullopt_t) noexcept;
>    >     constexpr optional(const optional&);
>    >     constexpr optional(optional&&) noexcept(see below);
>    >     […]

> 2. Modify 20.6.3.2 [optional.ctor] as indicated:

>    >     constexpr optional(const optional& rhs);

>    > > […]

>    > > -6- Remarks: This constructor shall be defined as deleted unless `is_copy_constructible_v<T>` is `true`. If `T` is a trivially copyable type, this constructor shall be a `constexpr` constructor.

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

>    > > […]

>    > > -10- 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 `T` is a trivially copyable type, this constructor shall be a `constexpr` constructor.

[Kona 2017-02-27]

Accepted as Immediate to resolve NB comment.

Proposed resolution:

This wording is relative to N4640.

1. Change the synopsis of class template `optional` in 20.6.3 [optional.optional] as follows:

   >     […]
   >     // 20.6.3.1, constructors
   >     constexpr optional() noexcept;
   >     constexpr optional(nullopt_t) noexcept;
   >     constexpr optional(const optional&);
   >     constexpr optional(optional&&) noexcept(see below);
   >     […]

2. Modify 20.6.3.2 [optional.ctor] as indicated:

   >     constexpr optional(const optional& rhs);

   > > […]

   > > -6- 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` constructor.

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

   > > […]

   > > -10- 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` constructor.