**Section:** 5.3 [fund.ts.v2::optional.object] **Status:** TS
**Submitter:** Geoffrey Romer **Opened:** 2014-10-31 **Last modified:** 2018-07-08 00:39:50 UTC

**Priority: **Not Prioritized

**View all other** issues in [fund.ts.v2::optional.object].

**View all issues with** TS status.

**Discussion:**

**Addresses: fund.ts.v2**

Code such as the following is currently ill-formed (thanks to STL for the compelling example):

optional<string> opt_str = "meow";

This is because it would require two user-defined conversions (from `const char*` to `string`,
and from `string` to `optional<string>`) where the language permits only one. This is
likely to be a surprise and an inconvenience for users.

`optional<T>` should be implicitly convertible from any `U` that is implicitly convertible
to `T`. This can be implemented as a non-explicit constructor template `optional(U&&)`,
which is enabled via SFINAE only if `is_convertible_v<U, T>` and `is_constructible_v<T, U>`,
plus any additional conditions needed to avoid ambiguity with other constructors (see
N4064, particularly the
"Odd" example, for why `is_convertible` and `is_constructible` are both needed; thanks to Howard
Hinnant for spotting this).

In addition, we may want to support explicit construction from `U`, which would mean providing a corresponding
explicit constructor with a complementary SFINAE condition (this is the single-argument case of the "perfect
initialization" pattern described in N4064).

*[2015-10, Kona Saturday afternoon]*

STL: This has status LEWG, but it should be priority 1, since we cannot ship an IS without this.

TK: We assigned our own priorities to LWG-LEWG issues, but haven't actually processed any issues yet.

MC: This is important.

*[2016-02-17, Ville comments and provides concrete wording]*

I have prototype-implemented this wording in libstdc++. I didn't edit
the copy/move-assignment operator tables into the new
`operator=` templates that take `optional`s of a different
type; there's a drafting note that suggests copying them
from the existing tables.

*[LEWG: 2016-03, Jacksonville]*

Discussion of whether `variant` supports this. We think it does.

Take it for C++17.

Unanimous yes.

**Proposed resolution:**

This wording is relative to N4562.

Edit 22.5.3 [optional.optional] as indicated:

template <class T> class optional { public: typedef T value_type;

*// 5.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> constexpr optional(const optional<U>&); template <class U> constexpr optional(optional<U>&&); […]*// 5.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&&...); […] };In 5.3.1 [fund.ts.v2::optional.object.ctor], insert new signature specifications after p33:

[

*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> constexpr 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*: 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, 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> constexpr 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*: 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<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`.In 5.3.3 [fund.ts.v2::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 valueassigns `*rhs`to the contained valueinitializes the contained value as if direct-non-list-initializing an object of type `T`with`*rhs``rhs`does not contain a valuedestroys 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 valueassigns `std::move(*rhs)`to the contained valueinitializes the contained value as if direct-non-list-initializing an object of type `T`with`std::move(*rhs)``rhs`does not contain a valuedestroys 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`.