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

`optional<T>`

should 'forward' `T`

's implicit conversions**Section:** 5.3 [fund.ts.v2::optional.object] **Status:** TS
**Submitter:** Geoffrey Romer **Opened:** 2014-10-31 **Last modified:** 2018-07-08

**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`

.