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.

### 2756. C++ WP `optional<T>` should 'forward' `T`'s implicit conversions

Section: 22.5.3 [optional.optional] Status: C++17 Submitter: Casey Carter Opened: 2016-07-26 Last modified: 2017-07-30

Priority: 1

View all other issues in [optional.optional].

View all issues with C++17 status.

Discussion:

LWG 2451 adds converting constructors and assignment operators to `optional`. The committee voted to apply it to the Library Fundamentals 2 TS WP in Oulu as part of LWG Motion 3. In both LWG and LEWG discussion of this issue, it was considered to be critical to apply to the specification of `optional` before shipping C++17 — it was an oversight on the part of LWG that there was no motion brought to apply this resolution to the C++ WP.

LWG 2745 proposes removal of the `constexpr` specifier from the declarations of the converting constructors from `const optional<U>&` and `optional<U>&&` since they are not implementable as `constexpr` constructors in C++17.

This issue proposes application of the resolution of LWG 2451 as amended by LWG 2745 to the specification of `optional` in the C++ WP.

[2016-07 — Chicago]

Monday: Priority set to 1; will re-review later in the week

[2016-08-03, Tomasz comments]

1. Value forwarding constructor (`template<typename U> optional(U&&)`) is underspecified.

   For the following use code:

   >     optional<T> o1;
   >     optional<T> o2(o1);

   The second constructor will invoke value forwarding `(U = optional<T>&)` constructor (better match) instead of the `optional<T>` copy constructor, in case if `T` can be constructed from `optional<T>`. This happens for any type `T` that has unconstrained perfect forwarding constructor, especially `optional<any>`.

2. The behavior of the construction of the `optional<T> ot` from `optional<U> ou` is inconsistent for classes `T` than can be constructed both from `optional<U>` and `U`. There are two possible semantics for such operation:

   - unwrapping: if `ou` is initialized (`bool(ou)`), initialize contained value of `ot` from `*ou`, otherwise make `ot` uninitialized (`!bool(ot)`)

   - value forwarding: initialize contained value of `ot` from `ou`, `ot` is always initialized (`bool(ot)`).

     With the proposed resolution, construction is preferring value forwarding, while assignment is always using unwrapping.

   For example, if we consider following class:

   >     struct Widget
   >     {
   >       Widget(int);
   >       Widget(optional<int>);
   >     };

   Notice, that such set of constructor is pretty common in situation when the construction of the `Widget` from known value is common and usage of `optional` version is rare. In such situation, presence of `Widget(int)` construction is an optimization used to avoid unnecessary empty checks and construction `optional<int>`.

   For the following declarations:

   >     optional<Widget> w1(make_optional(10));
   >     optional<Widget> w2;
   >     w2 = make_optional(10);

   The `w1` will contain a value created using `Widget(optional<int>)` constructor, as corresponding unwrapping constructor (`optional<U> const&`) is eliminated by `is_constructible_v<T, const optional<U>&>` (`is_constructible_v<Widget, const optional<int>&>`) having a `true` value. In contrast `w2` will contain a value created using `Widget(int)` constructor, as corresponding value forwarding assignment (`U&&`) is eliminated by the fact that `std::decay_t<U>` (`optional<int>`) is specialization of `optional`.

   In addition, the construction is having a preference for value forwarding and assignment is always using unwrapping. That means that for the following class:

   >     struct Thingy
   >     {
   >        Thingy(optional<string>);
   >     };

   >     optional<Thingy> t1(optional<string>("test"));

   The `t1` has a contained value constructed from using `Thingy(optional<std::string>)`, as unwrapping constructor (`optional<U> const&`) are eliminated by the `is_constructible<T, U const&>` (`is_constructible<Thingy, std::string const&>`) being `false`. However the following:

   >     t1 = optional<std::string>("test2");

   will not compile, because, the value forwarding assignment (`U&&`) is eliminated by `std::decay_t<U>` (`optional<std::string>`) being optional specialization and the unwrapping assignment (`optional<U> const&`) is ill-formed because `is_constructible<T, U const&>` (`is_constructible<Thingy, std::string const&>`) is `false`.

3. The semantics of construction and assignment, of `optional<optional<V>>` from `optional<U>` where `U` is convertible to/ same as `T` is also inconsistent. Firstly, in this situation the `optional<V>` is a type that can be constructed both from `optional<U>` and `U` so it fails into set of problem described above. However in addition we have following non-template constructor in `optional<T>`:

   >     optional(T const&);

   Which for `optional<optional<V>>` is equivalent to:

   >     optional(optional<V> const&);

   While there is no corresponding non-template assignment from `T const&`, to make sure that `o = {};` always clear an `optional o`.

   So for the following declarations:

   >     optional<int> oi;
   >     optional<short> os;

   >     optional<optional<int>> ooi1(oi);
   >     optional<optional<int>> ooi2(os);

   The `ooi1` uses from-`T` constructor, while `ooi2` uses value forwarding constructor. In this case both `ooi1` and `ooi2` are initialized and their contained values `*ooi1`, `*ooi2` are uninitialized `optional`s. However, if we will attempt to make construction consistent with assignment, by preferring unwrapping (`optional<U> const&`), then `ooi2` will end up being uninitialized.

   In summary, I believe that relation between unwrapping, value forwarding and from-`T` construction/assignment is to subtle to being handled as defect report and requires a full paper analyzing possible design and their pros/cons.

Tuesday PM: Ville and Eric to implement and report back in Issaquah. Moved to Open

Ville notes that 2746, 2754 and 2756 all go together.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4606.

> 1. Modify 22.5.3 [optional.optional] as indicated:

>    >     template <class T> class optional
>    >     {
>    >     public:
>    >       using value_type = T;

>    >       // 20.6.3.1, Constructors
>    >       constexpr optional() noexcept;
>    >       constexpr optional(nullopt_t) noexcept;
>    >       optional(const optional &);
>    >       optional(optional &&) noexcept(see below);
>    >       constexpr optional(const T &);
>    >       constexpr optional(T &&);
>    >       template <class... Args> constexpr explicit optional(in_place_t, Args &&...);
>    >       template <class U, class... Args>
>    >         constexpr explicit optional(in_place_t, initializer_list<U>, Args &&...);
>    >       template <class U> constexpr optional(U &&);
>    >       template <class U> optional(const optional<U> &);
>    >       template <class U> optional(optional<U> &&);

>    >       […]

>    >       // 20.6.3.3, Assignment
>    >       optional &operator=(nullopt_t) noexcept;
>    >       optional &operator=(const optional &);
>    >       optional &operator=(optional &&) noexcept(see below);
>    >       template <class U> optional &operator=(U &&);
>    >       template <class U> optional& operator=(const optional<U> &);
>    >       template <class U> optional& operator=(optional<U> &&);
>    >       template <class... Args> void emplace(Args &&...);
>    >       template <class U, class... Args>
>    >         void emplace(initializer_list<U>, Args &&...);

>    >       […]

>    >     };

> 2. In [optional.object.ctor], insert new signature specifications after p31:

>    > [Note: The following constructors are conditionally specified as `explicit`. This is typically implemented by declaring two such constructors, of which at most one participates in overload resolution. — end note]

>    >     template <class U>
>    >     constexpr optional(U&& v);

>    > > -?- Effects: Initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `std::forward<U>(v)`.

>    > > -?- Postconditions: `*this` contains a value.

>    > > -?- Throws: Any exception thrown by the selected constructor of `T`.

>    > > -?- Remarks: If `T`'s selected constructor is a `constexpr` constructor, this constructor shall be a `constexpr` constructor. This constructor shall not participate in overload resolution unless `is_constructible_v<T, U&&>` is `true` and `U` is not the same type as `T`. The constructor is `explicit` if and only if `is_convertible_v<U&&, T>` is `false`.

>    >     template <class U>
>    >     optional(const optional<U>& rhs);

>    > > -?- Effects: If `rhs` contains a value, initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `*rhs`.

>    > > -?- Postconditions: `bool(rhs) == bool(*this)`.

>    > > -?- Throws: Any exception thrown by the selected constructor of `T`.

>    > > -?- Remarks: This constructor shall not participate in overload resolution unless `is_constructible_v<T, const U&>` is `true`, `is_same<decay_t<U>, T>` is `false`, `is_constructible_v<T, const optional<U>&>` is `false` and `is_convertible_v<const optional<U>&, T>` is `false`. The constructor is `explicit` if and only if `is_convertible_v<const U&, T>` is `false`.

>    >     template <class U>
>    >     optional(optional<U>&& rhs);

>    > > -?- Effects: If `rhs` contains a value, initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `std::move(*rhs)`. `bool(rhs)` is unchanged.

>    > > -?- Postconditions: `bool(rhs) == bool(*this)`.

>    > > -?- Throws: Any exception thrown by the selected constructor of `T`.

>    > > -?- Remarks: This constructor shall not participate in overload resolution unless `is_constructible_v<T, U&&>` is `true`, `is_same<decay_t<U>, T>` is `false`, `is_constructible_v<T, optional<U>&&>` is `false` and `is_convertible_v<optional<U>&&, T>` is `false` and `U` is not the same type as `T`. The constructor is `explicit` if and only if `is_convertible_v<U&&, T>` is `false`.

> 3. In [optional.object.assign], change as indicated:

>    >     template <class U> optional<T>& operator=(U&& v);

>    > > -22- Remarks: If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s constructor, the state of `v` is determined by the exception safety guarantee of `T`'s constructor. If an exception is thrown during the call to `T`'s assignment, the state of `*val` and `v` is determined by the exception safety guarantee of `T`'s assignment. The function shall not participate in overload resolution unless `decay_t<U>` is not `nullopt_t` and `decay_t<U>` is not a specialization of `optional` `is_same_v<decay_t<U>, T>` is `true`.

>    > > -23- Notes: The reason for providing such generic assignment and then constraining it so that effectively `T == U` is to guarantee that assignment of the form `o = {}` is unambiguous.

>    >     template <class U> optional<T>& operator=(const optional<U>& rhs);

>    > > -?- Requires: `is_constructible_v<T, const U&>` is `true` and `is_assignable_v<T&, const U&>` is `true`.

>    > > -?- Effects:

>    > > Table ? — `optional::operator=(const optional<U>&)` effects
>    > > | `*this` contains a value | `*this` does not contain a value
>    > > --- | --- | ---
>    > > `rhs` contains a value | assigns `*rhs` to the contained value | initializes the contained value as if direct-non-list-initializing an object of type `T` with `*rhs`
>    > > `rhs` does not contain a value | destroys the contained value by calling `val->T::~T()` | no effect

>    > > -?- Returns: `*this`.

>    > > -?- Postconditions: `bool(rhs) == bool(*this)`.

>    > > -?- Remarks: If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s constructor, the state of `*rhs.val` is determined by the exception safety guarantee of `T`'s constructor. If an exception is thrown during the call to `T`'s assignment, the state of `*val` and `*rhs.val` is determined by the exception safety guarantee of `T`'s assignment. The function shall not participate in overload resolution unless `is_same_v<decay_t<U>, T>` is `false`.

>    >     template <class U> optional<T>& operator=(optional<U>&& rhs);

>    > > -?- Requires: `is_constructible_v<T, U>` is `true` and `is_assignable_v<T&, U>` is `true`.

>    > > -?- Effects: The result of the expression `bool(rhs)` remains unchanged.

>    > > Table ? — `optional::operator=(optional<U>&&)` effects
>    > > | `*this` contains a value | `*this` does not contain a value
>    > > --- | --- | ---
>    > > `rhs` contains a value | assigns `std::move(*rhs)` to the contained value | initializes the contained value as if direct-non-list-initializing an object of type `T` with `std::move(*rhs)`
>    > > `rhs` does not contain a value | destroys the contained value by calling `val->T::~T()` | no effect

>    > > -?- Returns: `*this`.

>    > > -?- Postconditions: `bool(rhs) == bool(*this)`.

>    > > -?- Remarks: If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s constructor, the state of `*rhs.val` is determined by the exception safety guarantee of `T`'s constructor. If an exception is thrown during the call to `T`'s assignment, the state of `*val` and `*rhs.val` is determined by the exception safety guarantee of `T`'s assignment. The function shall not participate in overload resolution unless `is_same_v<decay_t<U>, T>` is `false`.

[2016-08-05 Chicago LWG]

Ville provides revised wording, that also fixes LWG 2753.

Rationale:

1. The resolution of LWG 2753 makes special member functions defined as deleted in case the desired constraints aren't met.

2. There is no decay for the converting constructor `optional(U&&)`, there is a `remove_reference` instead. The target type may hold a cv-qualified type, and the incoming type may hold a cv-qualified type, but neither can hold a reference. Thus, `remove_reference` is what we need, `remove_cv` would be wrong, and `decay` would be wrong.

3. There is no `decay` or `remove_reference` for converting constructors like `optional(optional<U>)`, because none is needed.

4. For `optional(U&&)`, an added constraint is that `U` is not a specialization of `optional`

[2016-08, Chicago]

Fri PM: Move to Tentatively Ready

Previous resolution [SUPERSEDED]:

> This wording is relative to N4606.

> 1. Modify 22.5.3 [optional.optional] as indicated:

>    >     template <class T> class optional
>    >     {
>    >     public:
>    >       using value_type = T;

>    >       // 20.6.3.1, Constructors
>    >       constexpr optional() noexcept;
>    >       constexpr optional(nullopt_t) noexcept;
>    >       optional(const optional &);
>    >       optional(optional &&) noexcept(see below);
>    >       constexpr optional(const T &);
>    >       constexpr optional(T &&);
>    >       template <class... Args> constexpr explicit optional(in_place_t, Args &&...);
>    >       template <class U, class... Args>
>    >         constexpr explicit optional(in_place_t, initializer_list<U>, Args &&...);
>    >       template <class U> EXPLICIT constexpr optional(U &&);
>    >       template <class U> EXPLICIT optional(const optional<U> &);
>    >       template <class U> EXPLICIT optional(optional<U> &&);

>    >       […]

>    >       // 20.6.3.3, Assignment
>    >       optional &operator=(nullopt_t) noexcept;
>    >       optional &operator=(const optional &);
>    >       optional &operator=(optional &&) noexcept(see below);
>    >       template <class U> optional &operator=(U &&);
>    >       template <class U> optional& operator=(const optional<U> &);
>    >       template <class U> optional& operator=(optional<U> &&);
>    >       template <class... Args> void emplace(Args &&...);
>    >       template <class U, class... Args>
>    >         void emplace(initializer_list<U>, Args &&...);

>    >       […]

>    >     };

> 2. Change [optional.object.ctor] as indicated:

>    >     optional(const optional<T>& rhs);

>    > > -3- Requires: `is_copy_constructible_v<T>` is `true`.

>    > > […]

>    > > -?- Remarks: This constructor shall be defined as deleted unless `is_copy_constructible_v<T>` is `true`.

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

>    > > -7- Requires: `is_move_constructible_v<T>` is `true`.

>    > > […]

>    > > -11- Remarks: The expression inside `noexcept` is equivalent to `is_nothrow_move_constructible_v<T>`. This constructor shall be defined as deleted unless `is_move_constructible_v<T>` is `true`.

>    >     constexpr optional(const T& v);

>    > > -12- Requires: `is_copy_constructible_v<T>` is `true`.

>    > > […]

>    > > -16- Remarks: If `T`'s selected constructor is a `constexpr` constructor, this constructor shall be a `constexpr` constructor. This constructor shall not participate in overload resolution unless `is_copy_constructible_v<T>` is `true`.

>    >     constexpr optional(T&& v);

>    > > -17- Requires: `is_move_constructible_v<T>` is `true`.

>    > > […]

>    > > -21- Remarks: If `T`'s selected constructor is a `constexpr` constructor, this constructor shall be a `constexpr` constructor. This constructor shall not participate in overload resolution unless `is_move_constructible_v<T>` is `true`.

>    >     template <class... Args> constexpr explicit optional(in_place_t, Args&&... args);

>    > > -22- Requires: `is_constructible_v<T, Args&&...>` is `true`.

>    > > […]

>    > > -26- Remarks: If `T`'s constructor selected for the initialization is a `constexpr` constructor, this constructor shall be a `constexpr` constructor. This constructor shall not participate in overload resolution unless `is_constructible_v<T, Args...>` is `true`.

>    >     template <class U, class... Args>
>    >       constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);

>    > > -27- Requires: `is_constructible_v<T, initializer_list<U>&, Args&&...>` is `true`.

>    > > […]

>    > > -31- Remarks: The function constructor shall not participate in overload resolution unless `is_constructible_v<T, initializer_list<U>&, Args&&...>` is `true`. If `T`'s constructor selected for the initialization is a `constexpr` constructor, this constructor shall be a `constexpr` constructor.

>    > [Note: The following constructors are conditionally specified as explicit. This is typically implemented by declaring two such constructors, of which at most one participates in overload resolution. — end note]

>    >     template <class U>
>    >       EXPLICIT constexpr optional(U&& v);

>    > > -?- Effects: Initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `std::forward<U>(v)`.

>    > > -?- Postconditions: `*this` contains a value.

>    > > -?- Throws: Any exception thrown by the selected constructor of `T`.

>    > > -?- Remarks: If `T`'s selected constructor is a `constexpr` constructor, this constructor shall be a `constexpr` constructor. This constructor shall not participate in overload resolution unless `is_constructible_v<T, U&&>` is `true`, `remove_reference_t<U>` is not the same type as `T`, and `U` is not a specialization of `optional`. The constructor is explicit if and only if `is_convertible_v<U&&, T>` is `false`.

>    >     template <class U>
>    >       EXPLICIT optional(const optional<U>& rhs);

>    > > -?- Effects: If `rhs` contains a value, initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `*rhs`.

>    > > -?- Postconditions: `bool(rhs) == bool(*this)`.

>    > > -?- Throws: Any exception thrown by the selected constructor of `T`.

>    > > -?- Remarks: This constructor shall not participate in overload resolution unless `is_constructible_v<T, const U&>` is `true` and `is_same_v<U, T>` is `false`. The constructor is explicit if and only if `is_convertible_v<const U&, T>` is `false`.

>    >     template <class U>
>    >       EXPLICIT optional(optional<U>&& rhs);

>    > > -?- Effects: If `rhs` contains a value, initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `std::move(*rhs)`. `bool(rhs)` is unchanged.

>    > > -?- Postconditions: `bool(rhs) == bool(*this)`.

>    > > -?- Throws: Any exception thrown by the selected constructor of `T`.

>    > > -?- Remarks: This constructor shall not participate in overload resolution unless `is_constructible_v<T, U&&>` is `true` and `is_same_v<U, T>` is `false`. The constructor is explicit if and only if `is_convertible_v<U&&, T>` is `false`.

> 3. Change [optional.object.assign] as indicated:

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

>    > > -4- Requires: `is_copy_constructible_v<T>` is `true` and `is_copy_assignable_v<T>` is `true`.

>    > > […]

>    > > -8- Remarks: If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s copy constructor, no effect. If an exception is thrown during the call to `T`'s copy assignment, the state of its contained value is as defined by the exception safety guarantee of `T`'s copy assignment. This operator shall be defined as deleted unless `is_copy_constructible_v<T>` is `true` and `is_copy_assignable_v<T>` is `true`.

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

>    > > -9- Requires: `is_move_constructible_v<T>` is `true` and `is_move_assignable_v<T>` is `true`.

>    > > […]

>    > > -13- Remarks: The expression inside `noexcept` is equivalent to:

>    > > >     is_nothrow_move_assignable_v<T> && is_nothrow_move_constructible_v<T>

>    > > -14- If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s move constructor, the state of `*rhs.val` is determined by the exception safety guarantee of `T`'s move constructor. If an exception is thrown during the call to `T`'s move assignment, the state of `*val` and `*rhs.val` is determined by the exception safety guarantee of `T`'s move assignment. This operator shall be defined as deleted unless `is_move_constructible_v<T>` is `true` and `is_move_assignable_v<T>` is `true`.

>    >     template <class U> optional<T>& operator=(U&& v);

>    > > -15- Requires: `is_constructible_v<T, U>` is `true` and `is_assignable_v<T&, U>` is `true`.

>    > > […]

>    > > -19- Remarks: If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s constructor, the state of `v` is determined by the exception safety guarantee of `T`'s constructor. If an exception is thrown during the call to `T`'s assignment, the state of `*val` and `v` is determined by the exception safety guarantee of `T`'s assignment. The This function shall not participate in overload resolution unless `is_same_v<decay_t<U>, T>` `decay_t<U>` is not `nullopt_t`, `decay_t<U>` is not a specialization of `optional`, `is_constructible_v<T, U>` is `true` and `is_assignable_v<T&, U>` is `true`.

>    > > -20- Notes: The reason for providing such generic assignment and then constraining it so that effectively `T == U` is to guarantee that assignment of the form `o = {}` is unambiguous.

>    >     template <class U> optional<T>& operator=(const optional<U>& rhs);

>    > > -?- Effects:

>    > > Table ? — `optional::operator=(const optional<U>&)` effects
>    > > | `*this` contains a value | `*this` does not contain a value
>    > > --- | --- | ---
>    > > `rhs` contains a value | assigns `*rhs` to the contained value | initializes the contained value as if direct-non-list-initializing an object of type `T` with `*rhs`
>    > > `rhs` does not contain a value | destroys the contained value by calling `val->T::~T()` | no effect

>    > > -?- Returns: `*this`.

>    > > -?- Postconditions: `bool(rhs) == bool(*this)`.

>    > > -?- Remarks: If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s constructor, the state of `*rhs.val` is determined by the exception safety guarantee of `T`'s constructor. If an exception is thrown during the call to `T`'s assignment, the state of `*val` and `*rhs.val` is determined by the exception safety guarantee of `T`'s assignment. The function shall not participate in overload resolution unless `is_constructible_v<T, const U&>` is `true` and `is_assignable_v<T&, const U&>` is `true` and `is_same_v<U, T>` is `false`.

>    >     template <class U> optional<T>& operator=(optional<U>&& rhs);

>    > > -?- Effects: The result of the expression `bool(rhs)` remains unchanged.

>    > > Table ? — `optional::operator=(optional<U>&&)` effects
>    > > | `*this` contains a value | `*this` does not contain a value
>    > > --- | --- | ---
>    > > `rhs` contains a value | assigns `std::move(*rhs)` to the contained value | initializes the contained value as if direct-non-list-initializing an object of type `T` with `std::move(*rhs)`
>    > > `rhs` does not contain a value | destroys the contained value by calling `val->T::~T()` | no effect

>    > > -?- Returns: `*this`.

>    > > -?- Postconditions: `bool(rhs) == bool(*this)`.

>    > > -?- Remarks: If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s constructor, the state of `*rhs.val` is determined by the exception safety guarantee of `T`'s constructor. If an exception is thrown during the call to `T`'s assignment, the state of `*val` and `*rhs.val` is determined by the exception safety guarantee of `T`'s assignment. The function shall not participate in overload resolution unless `is_constructible_v<T, U>` is `true` and `is_assignable_v<T&, U>` is `true` and `is_same_v<U, T>` is `false`.

[2016-08-08 Ville reopens and provides improved wording]

This alternative proposed wording also resolves 2753.

The constructors that take a const `T&` or `T&&` are replaced by a constructor template that takes a `U&&` and defaults `U = T`. This allows copy-list-initialization with empty braces to still work:

>     optional<whatever> o = {}; // equivalent to initializing optional with nullopt

This resolution makes converting constructors and assignments have the same capabilities, including using arguments that can't be deduced. That is achieved by using a perfect-forwarding constructor and an assignment operator that default their argument to `T`. We don't need separate overloads for `T`, the overload for `U` does the job:

>     optional<vector<int>> ovi{{1, 2, 3}}; // still works
>     ovi = {4, 5, 6, 7}; // now works, didn't work before

Furthermore, this proposed wording makes `optional` "always unwrap". That is, the result of the following initializations is the same:

>     optional<optional<int>> oi = optional<int>();
>     optional<optional<int>> oi = optional<short>();

Both of those initializations initialize the `optional` wrapping another `optional` as if initializing with `nullopt`. Assignments do the same. These changes solve the issues pointed out by Tomasz Kamiński.

This P/R has been implemented and tested as a modification on top of libstdc++'s `optional`.

[2016-08-08 Ville and Tomasz collaborate and improve wording]

The suggested wording retains `optional`'s converting constructors and assignment operators, but provides sane results for the types Tomasz Kaminski depicts in previous discussions.

As opposed to the current P/R of this issue, which does "always unwrap", this P/R does "always value-forward unless the incoming type is exactly a type that a special member function takes by reference, and don't unwrap if a value-forwarder can take an optional by any kind of reference".

I and Tomasz believe this is the best compromise between the different desires, and thus the best outcome so far.

This P/R has been implemented and tested on libstdc++.

[2016-09-08 Casey Carter finetunes existing resolution for move members]

[2016-09-09 Issues Resolution Telecon]

Move to Tentatively Ready

[2016-10-06 Ville Voutilainen finetunes the resolution for assignment from scalars]

Previous resolution [SUPERSEDED]:

> This wording is relative to N4606.

> 1. Modify 22.5.3 [optional.optional] as indicated:

>    >     template <class T> class optional
>    >     {
>    >     public:
>    >       using value_type = T;

>    >       // 20.6.3.1, Constructors
>    >       constexpr optional() noexcept;
>    >       constexpr optional(nullopt_t) noexcept;
>    >       optional(const optional &);
>    >       optional(optional &&) noexcept(see below);
>    >       constexpr optional(const T &);
>    >       constexpr optional(T &&);
>    >       template <class... Args> constexpr explicit optional(in_place_t, Args &&...);
>    >       template <class U, class... Args>
>    >         constexpr explicit optional(in_place_t, initializer_list<U>, Args &&...);
>    >       template <class U = T> EXPLICIT constexpr optional(U &&);
>    >       template <class U> EXPLICIT optional(const optional<U> &);
>    >       template <class U> EXPLICIT optional(optional<U> &&);

>    >       […]

>    >       // 20.6.3.3, Assignment
>    >       optional &operator=(nullopt_t) noexcept;
>    >       optional &operator=(const optional &);
>    >       optional &operator=(optional &&) noexcept(see below);
>    >       template <class U = T> optional &operator=(U &&);
>    >       template <class U> optional& operator=(const optional<U> &);
>    >       template <class U> optional& operator=(optional<U> &&);
>    >       template <class... Args> void emplace(Args &&...);
>    >       template <class U, class... Args>
>    >         void emplace(initializer_list<U>, Args &&...);

>    >       […]

>    >     };

> 2. Change [optional.object.ctor] as indicated:

>    >     optional(const optional<T>& rhs);

>    > > -3- Requires: `is_copy_constructible_v<T>` is `true`.

>    > > […]

>    > > -?- Remarks: This constructor shall be defined as deleted unless `is_copy_constructible_v<T>` is `true`.

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

>    > > -7- Requires: `is_move_constructible_v<T>` is `true`.

>    > > […]

>    > > -11- 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`.

>    >     constexpr optional(const T& v);

>    > > -12- Requires: `is_copy_constructible_v<T>` is `true`.

>    > > -13- Effects: Initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `v`.

>    > > -14- Postcondition: `*this` contains a value.

>    > > -15- Throws: Any exception thrown by the selected constructor of `T`.

>    > > -16- Remarks: If `T`'s selected constructor is a `constexpr` constructor, this constructor shall be a `constexpr` constructor.

>    >     constexpr optional(T&& v);

>    > > -17- Requires: `is_move_constructible_v<T>` is `true`.

>    > > -18- Effects: Initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `std::move(v)`.

>    > > -19- Postcondition: `*this` contains a value.

>    > > -20- Throws: Any exception thrown by the selected constructor of `T`.

>    > > -21- Remarks: If `T`'s selected constructor is a `constexpr` constructor, this constructor shall be a `constexpr` constructor.

>    >     template <class... Args> constexpr explicit optional(in_place_t, Args&&... args);

>    > > -22- Requires: `is_constructible_v<T, Args&&...>` is `true`.

>    > > […]

>    > > -26- Remarks: If `T`'s constructor selected for the initialization is a `constexpr` constructor, this constructor shall be a `constexpr` constructor. This constructor shall not participate in overload resolution unless `is_constructible_v<T, Args...>` is `true`.

>    >     template <class U, class... Args>
>    >       constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);

>    > > -27- Requires: `is_constructible_v<T, initializer_list<U>&, Args&&...>` is `true`.

>    > > […]

>    > > -31- Remarks: The function This constructor shall not participate in overload resolution unless `is_constructible_v<T, initializer_list<U>&, Args&&...>` is `true`. If `T`'s constructor selected for the initialization is a `constexpr` constructor, this constructor shall be a `constexpr` constructor.

>    > [Note: The following constructors are conditionally specified as explicit. This is typically implemented by declaring two such constructors, of which at most one participates in overload resolution. — end note]

>    >     template <class U = T>
>    >       EXPLICIT constexpr optional(U&& v);

>    > > -?- Effects: Initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `std::forward<U>(v)`.

>    > > -?- Postconditions: `*this` contains a value.

>    > > -?- Throws: Any exception thrown by the selected constructor of `T`.

>    > > -?- Remarks: If `T`'s selected constructor is a `constexpr` constructor, this constructor shall be a `constexpr` constructor. This constructor shall not participate in overload resolution unless `is_constructible_v<T, U&&>` is `true`, `is_same_v<U, in_place_t>` is `false`, and `is_same_v<optional<T>, decay_t<U>>` is `false`. The constructor is explicit if and only if `is_convertible_v<U&&, T>` is `false`.

>    >     template <class U>
>    >       EXPLICIT optional(const optional<U>& rhs);

>    > > -?- Effects: If `rhs` contains a value, initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `*rhs`.

>    > > -?- Postconditions: `bool(rhs) == bool(*this)`.

>    > > -?- Throws: Any exception thrown by the selected constructor of `T`.

>    > > -?- Remarks: This constructor shall not participate in overload resolution unless `is_constructible_v<T, const U&>` is `true`, `is_constructible_v<T, optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&&>` is `false`, `is_constructible_v<T, optional<U>&&>` is `false`, `is_convertible_v<optional<U>&, T>` is `false`, `is_convertible_v<const optional<U>&, T>` is `false`, `is_convertible_v<const optional<U>&&, T>` is `false`, and `is_convertible_v<optional<U>&&, T>` is `false`. The constructor is explicit if and only if `is_convertible_v<const U&, T>` is `false`.

>    >     template <class U>
>    >       EXPLICIT optional(optional<U>&& rhs);

>    > > -?- Effects: If `rhs` contains a value, initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `std::move(*rhs)`. `bool(rhs)` is unchanged.

>    > > -?- Postconditions: `bool(rhs) == bool(*this)`.

>    > > -?- Throws: Any exception thrown by the selected constructor of `T`.

>    > > -?- Remarks: This constructor shall not participate in overload resolution unless `is_constructible_v<T, U&&>` is `true`, `is_constructible_v<T, optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&&>` is `false`, `is_constructible_v<T, optional<U>&&>` is `false`, `is_convertible_v<optional<U>&, T>` is `false`, `is_convertible_v<const optional<U>&, T>` is `false`, `is_convertible_v<const optional<U>&&, T>` is `false`, and `is_convertible_v<optional<U>&&, T>` is `false`. The constructor is explicit if and only if `is_convertible_v<U&&, T>` is `false`.

> 3. Change [optional.object.assign] as indicated:

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

>    > > -4- Requires: `is_copy_constructible_v<T>` is `true` and `is_copy_assignable_v<T>` is `true`.

>    > > […]

>    > > -8- Remarks: If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s copy constructor, no effect. If an exception is thrown during the call to `T`'s copy assignment, the state of its contained value is as defined by the exception safety guarantee of `T`'s copy assignment. This operator shall be defined as deleted unless `is_copy_constructible_v<T>` is `true` and `is_copy_assignable_v<T>` is `true`.

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

>    > > -9- Requires: `is_move_constructible_v<T>` is `true` and `is_move_assignable_v<T>` is `true`.

>    > > […]

>    > > -13- Remarks: The expression inside `noexcept` is equivalent to:

>    > > >     is_nothrow_move_assignable_v<T> && is_nothrow_move_constructible_v<T>

>    > > -14- If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s move constructor, the state of `*rhs.val` is determined by the exception safety guarantee of `T`'s move constructor. If an exception is thrown during the call to `T`'s move assignment, the state of `*val` and `*rhs.val` is determined by the exception safety guarantee of `T`'s move assignment. This operator shall not participate in overload resolution unless `is_move_constructible_v<T>` is `true` and `is_move_assignable_v<T>` is `true`.

>    >     template <class U = T> optional<T>& operator=(U&& v);

>    > > -15- Requires: `is_constructible_v<T, U>` is `true` and `is_assignable_v<T&, U>` is `true`.

>    > > […]

>    > > -19- Remarks: If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s constructor, the state of `v` is determined by the exception safety guarantee of `T`'s constructor. If an exception is thrown during the call to `T`'s assignment, the state of `*val` and `v` is determined by the exception safety guarantee of `T`'s assignment. The This function shall not participate in overload resolution unless `is_same_v<decay_t<U>, T>` `is_same_v<optional<T>, decay_t<U>>` is `false`, `is_constructible_v<T, U>` is `true`, and `is_assignable_v<T&, U>` is `true`.

>    > > -20- Notes: The reason for providing such generic assignment and then constraining it so that effectively `T == U` is to guarantee that assignment of the form `o = {}` is unambiguous.

>    >     template <class U> optional<T>& operator=(const optional<U>& rhs);

>    > > -?- Effects: See Table ?.

>    > > Table ? — `optional::operator=(const optional<U>&)` effects
>    > > | `*this` contains a value | `*this` does not contain a value
>    > > --- | --- | ---
>    > > `rhs` contains a value | assigns `*rhs` to the contained value | initializes the contained value as if direct-non-list-initializing an object of type `T` with `*rhs`
>    > > `rhs` does not contain a value | destroys the contained value by calling `val->T::~T()` | no effect

>    > > -?- Returns: `*this`.

>    > > -?- Postconditions: `bool(rhs) == bool(*this)`.

>    > > -?- Remarks: If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s constructor, the state of `*rhs.val` is determined by the exception safety guarantee of `T`'s constructor. If an exception is thrown during the call to `T`'s assignment, the state of `*val` and `*rhs.val` is determined by the exception safety guarantee of `T`'s assignment. This function shall not participate in overload resolution unless `is_constructible_v<T, const U&>` is `true`, `is_assignable_v<T&, const U&>` is `true`, `is_constructible_v<T, optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&&>` is `false`, `is_constructible_v<T, optional<U>&&>` is `false`, `is_convertible_v<optional<U>&, T>` is `false`, `is_convertible_v<const optional<U>&, T>` is `false`, `is_convertible_v<const optional<U>&&, T>` is `false`, `is_convertible_v<optional<U>&&, T>` is `false`, `is_assignable_v<T&, optional<U>&>` is `false`, `is_assignable_v<T&, const optional<U>&>` is `false`, `is_assignable_v<T&, const optional<U>&&>` is `false`, and `is_assignable_v<T&, optional<U>&&>` is `false`.

>    >     template <class U> optional<T>& operator=(optional<U>&& rhs);

>    > > -?- Effects: See Table ?. The result of the expression `bool(rhs)` remains unchanged.

>    > > Table ? — `optional::operator=(optional<U>&&)` effects
>    > > | `*this` contains a value | `*this` does not contain a value
>    > > --- | --- | ---
>    > > `rhs` contains a value | assigns `std::move(*rhs)` to the contained value | initializes the contained value as if direct-non-list-initializing an object of type `T` with `std::move(*rhs)`
>    > > `rhs` does not contain a value | destroys the contained value by calling `val->T::~T()` | no effect

>    > > -?- Returns: `*this`.

>    > > -?- Postconditions: `bool(rhs) == bool(*this)`.

>    > > -?- Remarks: If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s constructor, the state of `*rhs.val` is determined by the exception safety guarantee of `T`'s constructor. If an exception is thrown during the call to `T`'s assignment, the state of `*val` and `*rhs.val` is determined by the exception safety guarantee of `T`'s assignment. This function shall not participate in overload resolution unless `is_constructible_v<T, U>` is `true`, `is_assignable_v<T&, U>` is `true`, `is_constructible_v<T, optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&&>` is `false`, `is_constructible_v<T, optional<U>&&>` is `false`, `is_convertible_v<optional<U>&, T>` is `false`, `is_convertible_v<const optional<U>&, T>` is `false`, `is_convertible<const optional<U>&&, T>` is `false`, `is_convertible<optional<U>&&, T>` is `false`, `is_assignable_v<T&, optional<U>&>` is `false`, `is_assignable_v<T&, const optional<U>&>` is `false`, `is_assignable_v<T&, const optional<U>&&>` is `false`, and `is_assignable_v<T&, optional<U>&&>` is `false`.

Proposed resolution:

This wording is relative to N4606.

1. Modify 22.5.3 [optional.optional] as indicated:

   >     template <class T> class optional
   >     {
   >     public:
   >       using value_type = T;

   >       // 20.6.3.1, Constructors
   >       constexpr optional() noexcept;
   >       constexpr optional(nullopt_t) noexcept;
   >       optional(const optional &);
   >       optional(optional &&) noexcept(see below);
   >       constexpr optional(const T &);
   >       constexpr optional(T &&);
   >       template <class... Args> constexpr explicit optional(in_place_t, Args &&...);
   >       template <class U, class... Args>
   >         constexpr explicit optional(in_place_t, initializer_list<U>, Args &&...);
   >       template <class U = T> EXPLICIT constexpr optional(U &&);
   >       template <class U> EXPLICIT optional(const optional<U> &);
   >       template <class U> EXPLICIT optional(optional<U> &&);

   >       […]

   >       // 20.6.3.3, Assignment
   >       optional &operator=(nullopt_t) noexcept;
   >       optional &operator=(const optional &);
   >       optional &operator=(optional &&) noexcept(see below);
   >       template <class U = T> optional &operator=(U &&);
   >       template <class U> optional& operator=(const optional<U> &);
   >       template <class U> optional& operator=(optional<U> &&);
   >       template <class... Args> void emplace(Args &&...);
   >       template <class U, class... Args>
   >         void emplace(initializer_list<U>, Args &&...);

   >       […]

   >     };

2. Change [optional.object.ctor] as indicated:

   >     optional(const optional<T>& rhs);

   > > -3- Requires: `is_copy_constructible_v<T>` is `true`.

   > > […]

   > > -?- Remarks: This constructor shall be defined as deleted unless `is_copy_constructible_v<T>` is `true`.

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

   > > -7- Requires: `is_move_constructible_v<T>` is `true`.

   > > […]

   > > -11- 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`.

   >     constexpr optional(const T& v);

   > > -12- Requires: `is_copy_constructible_v<T>` is `true`.

   > > -13- Effects: Initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `v`.

   > > -14- Postcondition: `*this` contains a value.

   > > -15- Throws: Any exception thrown by the selected constructor of `T`.

   > > -16- Remarks: If `T`'s selected constructor is a `constexpr` constructor, this constructor shall be a `constexpr` constructor.

   >     constexpr optional(T&& v);

   > > -17- Requires: `is_move_constructible_v<T>` is `true`.

   > > -18- Effects: Initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `std::move(v)`.

   > > -19- Postcondition: `*this` contains a value.

   > > -20- Throws: Any exception thrown by the selected constructor of `T`.

   > > -21- Remarks: If `T`'s selected constructor is a `constexpr` constructor, this constructor shall be a `constexpr` constructor.

   >     template <class... Args> constexpr explicit optional(in_place_t, Args&&... args);

   > > -22- Requires: `is_constructible_v<T, Args&&...>` is `true`.

   > > […]

   > > -26- Remarks: If `T`'s constructor selected for the initialization is a `constexpr` constructor, this constructor shall be a `constexpr` constructor. This constructor shall not participate in overload resolution unless `is_constructible_v<T, Args...>` is `true`.

   >     template <class U, class... Args>
   >       constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);

   > > -27- Requires: `is_constructible_v<T, initializer_list<U>&, Args&&...>` is `true`.

   > > […]

   > > -31- Remarks: The function This constructor shall not participate in overload resolution unless `is_constructible_v<T, initializer_list<U>&, Args&&...>` is `true`. If `T`'s constructor selected for the initialization is a `constexpr` constructor, this constructor shall be a `constexpr` constructor.

   > [Note: The following constructors are conditionally specified as explicit. This is typically implemented by declaring two such constructors, of which at most one participates in overload resolution. — end note]

   >     template <class U = T>
   >       EXPLICIT constexpr optional(U&& v);

   > > -?- Effects: Initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `std::forward<U>(v)`.

   > > -?- Postconditions: `*this` contains a value.

   > > -?- Throws: Any exception thrown by the selected constructor of `T`.

   > > -?- Remarks: If `T`'s selected constructor is a `constexpr` constructor, this constructor shall be a `constexpr` constructor. This constructor shall not participate in overload resolution unless `is_constructible_v<T, U&&>` is `true`, `is_same_v<U, in_place_t>` is `false`, and `is_same_v<optional<T>, decay_t<U>>` is `false`. The constructor is explicit if and only if `is_convertible_v<U&&, T>` is `false`.

   >     template <class U>
   >       EXPLICIT optional(const optional<U>& rhs);

   > > -?- Effects: If `rhs` contains a value, initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `*rhs`.

   > > -?- Postconditions: `bool(rhs) == bool(*this)`.

   > > -?- Throws: Any exception thrown by the selected constructor of `T`.

   > > -?- Remarks: This constructor shall not participate in overload resolution unless `is_constructible_v<T, const U&>` is `true`, `is_constructible_v<T, optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&&>` is `false`, `is_constructible_v<T, optional<U>&&>` is `false`, `is_convertible_v<optional<U>&, T>` is `false`, `is_convertible_v<const optional<U>&, T>` is `false`, `is_convertible_v<const optional<U>&&, T>` is `false`, and `is_convertible_v<optional<U>&&, T>` is `false`. The constructor is explicit if and only if `is_convertible_v<const U&, T>` is `false`.

   >     template <class U>
   >       EXPLICIT optional(optional<U>&& rhs);

   > > -?- Effects: If `rhs` contains a value, initializes the contained value as if direct-non-list-initializing an object of type `T` with the expression `std::move(*rhs)`. `bool(rhs)` is unchanged.

   > > -?- Postconditions: `bool(rhs) == bool(*this)`.

   > > -?- Throws: Any exception thrown by the selected constructor of `T`.

   > > -?- Remarks: This constructor shall not participate in overload resolution unless `is_constructible_v<T, U&&>` is `true`, `is_constructible_v<T, optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&&>` is `false`, `is_constructible_v<T, optional<U>&&>` is `false`, `is_convertible_v<optional<U>&, T>` is `false`, `is_convertible_v<const optional<U>&, T>` is `false`, `is_convertible_v<const optional<U>&&, T>` is `false`, and `is_convertible_v<optional<U>&&, T>` is `false`. The constructor is explicit if and only if `is_convertible_v<U&&, T>` is `false`.

3. Change [optional.object.assign] as indicated:

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

   > > -4- Requires: `is_copy_constructible_v<T>` is `true` and `is_copy_assignable_v<T>` is `true`.

   > > […]

   > > -8- Remarks: If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s copy constructor, no effect. If an exception is thrown during the call to `T`'s copy assignment, the state of its contained value is as defined by the exception safety guarantee of `T`'s copy assignment. This operator shall be defined as deleted unless `is_copy_constructible_v<T>` is `true` and `is_copy_assignable_v<T>` is `true`.

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

   > > -9- Requires: `is_move_constructible_v<T>` is `true` and `is_move_assignable_v<T>` is `true`.

   > > […]

   > > -13- Remarks: The expression inside `noexcept` is equivalent to:

   > > >     is_nothrow_move_assignable_v<T> && is_nothrow_move_constructible_v<T>

   > > -14- If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s move constructor, the state of `*rhs.val` is determined by the exception safety guarantee of `T`'s move constructor. If an exception is thrown during the call to `T`'s move assignment, the state of `*val` and `*rhs.val` is determined by the exception safety guarantee of `T`'s move assignment. This operator shall not participate in overload resolution unless `is_move_constructible_v<T>` is `true` and `is_move_assignable_v<T>` is `true`.

   >     template <class U = T> optional<T>& operator=(U&& v);

   > > -15- Requires: `is_constructible_v<T, U>` is `true` and `is_assignable_v<T&, U>` is `true`.

   > > […]

   > > -19- Remarks: If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s constructor, the state of `v` is determined by the exception safety guarantee of `T`'s constructor. If an exception is thrown during the call to `T`'s assignment, the state of `*val` and `v` is determined by the exception safety guarantee of `T`'s assignment. The This function shall not participate in overload resolution unless `is_same_v<decay_t<U>, T>` `is_same_v<optional<T>, decay_t<U>>` is `false`, conjunction_v<is_scalar<T>, is_same<T, decay_t<U>>> is `false`, `is_constructible_v<T, U>` is `true`, and `is_assignable_v<T&, U>` is `true`.

   > > -20- Notes: The reason for providing such generic assignment and then constraining it so that effectively `T == U` is to guarantee that assignment of the form `o = {}` is unambiguous.

   >     template <class U> optional<T>& operator=(const optional<U>& rhs);

   > > -?- Effects: See Table ?.

   > > Table ? — `optional::operator=(const optional<U>&)` effects
   > > | `*this` contains a value | `*this` does not contain a value
   > > --- | --- | ---
   > > `rhs` contains a value | assigns `*rhs` to the contained value | initializes the contained value as if direct-non-list-initializing an object of type `T` with `*rhs`
   > > `rhs` does not contain a value | destroys the contained value by calling `val->T::~T()` | no effect

   > > -?- Returns: `*this`.

   > > -?- Postconditions: `bool(rhs) == bool(*this)`.

   > > -?- Remarks: If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s constructor, the state of `*rhs.val` is determined by the exception safety guarantee of `T`'s constructor. If an exception is thrown during the call to `T`'s assignment, the state of `*val` and `*rhs.val` is determined by the exception safety guarantee of `T`'s assignment. This function shall not participate in overload resolution unless `is_constructible_v<T, const U&>` is `true`, `is_assignable_v<T&, const U&>` is `true`, `is_constructible_v<T, optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&&>` is `false`, `is_constructible_v<T, optional<U>&&>` is `false`, `is_convertible_v<optional<U>&, T>` is `false`, `is_convertible_v<const optional<U>&, T>` is `false`, `is_convertible_v<const optional<U>&&, T>` is `false`, `is_convertible_v<optional<U>&&, T>` is `false`, `is_assignable_v<T&, optional<U>&>` is `false`, `is_assignable_v<T&, const optional<U>&>` is `false`, `is_assignable_v<T&, const optional<U>&&>` is `false`, and `is_assignable_v<T&, optional<U>&&>` is `false`.

   >     template <class U> optional<T>& operator=(optional<U>&& rhs);

   > > -?- Effects: See Table ?. The result of the expression `bool(rhs)` remains unchanged.

   > > Table ? — `optional::operator=(optional<U>&&)` effects
   > > | `*this` contains a value | `*this` does not contain a value
   > > --- | --- | ---
   > > `rhs` contains a value | assigns `std::move(*rhs)` to the contained value | initializes the contained value as if direct-non-list-initializing an object of type `T` with `std::move(*rhs)`
   > > `rhs` does not contain a value | destroys the contained value by calling `val->T::~T()` | no effect

   > > -?- Returns: `*this`.

   > > -?- Postconditions: `bool(rhs) == bool(*this)`.

   > > -?- Remarks: If any exception is thrown, the result of the expression `bool(*this)` remains unchanged. If an exception is thrown during the call to `T`'s constructor, the state of `*rhs.val` is determined by the exception safety guarantee of `T`'s constructor. If an exception is thrown during the call to `T`'s assignment, the state of `*val` and `*rhs.val` is determined by the exception safety guarantee of `T`'s assignment. This function shall not participate in overload resolution unless `is_constructible_v<T, U>` is `true`, `is_assignable_v<T&, U>` is `true`, `is_constructible_v<T, optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&>` is `false`, `is_constructible_v<T, const optional<U>&&>` is `false`, `is_constructible_v<T, optional<U>&&>` is `false`, `is_convertible_v<optional<U>&, T>` is `false`, `is_convertible_v<const optional<U>&, T>` is `false`, `is_convertible<const optional<U>&&, T>` is `false`, `is_convertible<optional<U>&&, T>` is `false`, `is_assignable_v<T&, optional<U>&>` is `false`, `is_assignable_v<T&, const optional<U>&>` is `false`, `is_assignable_v<T&, const optional<U>&&>` is `false`, and `is_assignable_v<T&, optional<U>&&>` is `false`.