Created on 2023-11-24.00:00:00 last changed 1 month ago
Proposed resolution:
This wording is relative to N4988.
Modify [optional.optional.general], class template optional synopsis, as indicated:
namespace std { template<class T> class optional { public: using value_type = T; […] private:*val // exposition only; union { remove_cv_t<T> val; // exposition only }; }; […] }
Modify [optional.optional.general] as indicated:
-1- When its member val is active ([class.union.general]), an instance of
optional<T>
is said to contain a value, and val is referred to as its contained value.Any instance ofAn optional object's contained valueoptional<T>
at any given time either contains a value or does not contain a value. When an instance ofoptional<T>
contains a value, it means that an object of typeT
, referred to as thecontained value,is allocated within the storage of the optional object. Implementations are not permitted to use additional storage, such as dynamic memory, to allocate its contained value.When an object of typeoptional<T>
is contextually converted tobool
, the conversion returns `true` if the object contains a value; otherwise the conversion returns `false`.
-2- When an optional<T> object contains a value, member val points to the contained value.
Modify [optional.ctor] as indicated:
constexpr optional(const optional& rhs);-4- Effects: If rhs contains a value, direct-non-list-initializes val
-5- Postconditions: rhs.has_value() == this->has_value(). […]the contained valuewith*rhs.val.constexpr optional(optional&& rhs) noexcept(see below);-8- Constraints: […]
-9- Effects: If rhs contains a value, direct-non-list-initializes valthe contained valuewith std::move(*rhs.val). rhs.has_value() is unchanged. -10- Postconditions: rhs.has_value() == this->has_value(). […]template<class... Args> constexpr explicit optional(in_place_t, Args&&... args);-13- Constraints: […]
-14- Effects: Direct-non-list-initializes valthe contained valuewith std::forward<Args>(args).... -15- Postconditions: *this contains a value. […]template<class U, class... Args> constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);-18- Constraints: […]
-19- Effects: Direct-non-list-initializes valthe contained valuewith il, std::forward<Args>(args).... -20- Postconditions: *this contains a value. […]template<class U = T> constexpr explicit(see below) optional(U&& v);-23- Constraints: […]
-24- Effects: Direct-non-list-initializes valthe contained valuewith std::forward<U>(v). -25- Postconditions: *this contains a value. […]template<class U> constexpr explicit(see below) optional(const optional<U>& rhs);-28- Constraints: […]
-29- Effects: If rhs contains a value, direct-non-list-initializes valthe contained valuewith*rhs.val. -30- Postconditions: rhs.has_value() == this->has_value(). […]template<class U> constexpr explicit(see below) optional(optional<U>&& rhs);-33- Constraints: […]
-34- Effects: If rhs contains a value, direct-non-list-initializes valthe contained valuewith std::move(*rhs.val). rhs.has_value() is unchanged. -35- Postconditions: rhs.has_value() == this->has_value(). […]
Modify [optional.dtor] as indicated:
constexpr ~optional();-1- Effects: If is_trivially_destructible_v<T> != true and *this contains a value, calls
val->val.T::~T().
Modify [optional.assign] as indicated:
constexpr optional<T>& operator=(nullopt_t) noexcept;-1- Effects: If *this contains a value, calls
-2- Postconditions: *this does not contain a value.val->val.T::~T() to destroy the contained value; otherwise no effect.constexpr optional<T>& operator=(const optional& rhs);-4- Effects: See Table 58.
Table 58 — optional::operator=(const optional&) effects [tab:optional.assign.copy] *this contains a value *this does not contain a value rhs contains a value assigns *rhs.val to valthe contained valuedirect-non-list-initializes val the contained valuewith*rhs.val
rhs does not contain a value destroys the contained value by calling val->val.T::~T()
no effect -5- Postconditions: rhs.has_value() == this->has_value().
[…]constexpr optional<T>& operator=(optional&& rhs) noexcept(see below);-8- Constraints: […]
-9- Effects: See Table 59. The result of the expression rhs.has_value() remains unchanged. -10- Postconditions: rhs.has_value() == this->has_value(). -11- Returns: *this.
Table 59 — optional::operator=(optional&&) effects [tab:optional.assign.move] *this contains a value *this does not contain a value rhs contains a value assigns std::move( *rhs.val) to valthe contained valuedirect-non-list-initializes val the contained valuewith std::move(*rhs.val)rhs does not contain a value destroys the contained value by calling
val->val.T::~T()no effect -12- Remarks: […]
-13- If any exception is thrown, the result of the expression this->has_value() remains unchanged. If an exception is thrown during the call to T's move constructor, the state of*rhs.valvalis 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 statesstateof*valvaland*rhs.valvalareisdetermined by the exception safety guarantee of T's move assignment.template<class U = T> constexpr optional<T>& operator=(U&& v);-14- Constraints: […]
-15- Effects: If *this contains a value, assigns std::forward<U>(v) to valthe contained value; otherwise direct-non-list-initializes valthe contained valuewith std::forward<U>(v). -16- Postconditions: *this contains a value. -17- Returns: *this. -18- Remarks: If any exception is thrown, the result of the expression this->has_value() 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 statesstateof val*valand v areisdetermined by the exception safety guarantee of T's assignment.template<class U> constexpr optional<T>& operator=(const optional<U>& rhs);-19- Constraints: […]
-20- Effects: See Table 60.
Table 60 — optional::operator=(const optional<U>&) effects [tab:optional.assign.copy.templ] *this contains a value *this does not contain a value rhs contains a value assigns *rhs.val to valthe contained valuedirect-non-list-initializes val the contained valuewith*rhs.valrhs does not contain a value destroys the contained value by calling
val->val.T::~T()no effect -21- Postconditions: rhs.has_value() == this->has_value().
-22- Returns: *this. -23- If any exception is thrown, the result of the expression this->has_value() remains unchanged. If an exception is thrown during the call to T's constructor, the state of*rhs.valvalis determined by the exception safety guarantee of T's constructor. If an exception is thrown during the call to T's assignment, the statesstateof val*valand*rhs.valvalareisdetermined by the exception safety guarantee of T's assignment.template<class U> constexpr optional<T>& operator=(optional<U>&& rhs);-24- Constraints: […]
-25- Effects: See Table 61. The result of the expression rhs.has_value() remains unchanged.
Table 61 — optional::operator=(optional<U>&&) effects [tab:optional.assign.move.templ] *this contains a value *this does not contain a value rhs contains a value assigns std::move( *rhs.val) to valthe contained valuedirect-non-list-initializes val the contained valuewith
std::move(*rhs.val)rhs does not contain a value destroys the contained value by calling
val->val.T::~T()no effect -26- Postconditions: rhs.has_value() == this->has_value().
-27- Returns: *this. -28- If any exception is thrown, the result of the expression this->has_value() remains unchanged. If an exception is thrown during the call to T's constructor, the state of*rhs.valvalis determined by the exception safety guarantee of T's constructor. If an exception is thrown during the call to T's assignment, the statesstateof val*valand*rhs.valvalareisdetermined by the exception safety guarantee of T's assignment.template<class... Args> constexpr T& emplace(Args&&... args);-29- Mandates: […]
-30- Effects: Calls *this = nullopt. Then direct-non-list-initializes valthe contained valuewith std::forward<Args>(args).... -31- Postconditions: *this contains a value. -32- Returns: valA reference to the new contained value. […] -34- Remarks: If an exception is thrown during the call to T's constructor, *this does not contain a value, and the previous val*val(if any) has been destroyed.template<class U, class... Args> constexpr T& emplace(initializer_list<U> il, Args&&... args);-35- Constraints: […]
-36- Effects: Calls *this = nullopt. Then direct-non-list-initializes valthe contained valuewith il, std::forward<Args>(args).... -37- Postconditions: *this contains a value. -38- Returns: valA reference to the new contained value. […] -40- Remarks: If an exception is thrown during the call to T's constructor, *this does not contain a value, and the previous val*val(if any) has been destroyed.
Modify [optional.swap] as indicated:
constexpr void swap(optional& rhs) noexcept(see below);-1- Mandates: […]
-2- Preconditions: […] -3- Effects: See Table 62.
Table 62 — optional::swap(optional&) effects [tab:optional.swap] *this contains a value *this does not contain a value rhs contains a value calls swap(val *(*this),*rhs.val)direct-non-list-initializes val the contained value of *this
with std::move(*rhs.val), followed by rhs.val.val->T::~T();
postcondition is that *this contains a value and rhs does
not contain a valuerhs does not contain a value direct-non-list-initializes the contained value ofrhs.val
with std::move(val*(*this)), followed by val.val->T::~T();
postcondition is that *this does not contain a value and rhs
contains a valueno effect -4- Throws: […]
-5- Remarks: […] -6- If any exception is thrown, the results of the expressions this->has_value() and rhs.has_value() remain unchanged. If an exception is thrown during the call to function swap, the state of val*valand*rhs.valvalis determined by the exception safety guarantee of swap for lvalues of T. If an exception is thrown during the call to T's move constructor, the statesstateof val*valand*rhs.valvalareisdetermined by the exception safety guarantee of T's move constructor.
Modify [optional.observe] as indicated:
constexpr const T* operator->() const noexcept; constexpr T* operator->() noexcept;-1- Preconditions: *this contains a value.
-2- Returns: addressof(val)val. -3- […]constexpr const T& operator*() const & noexcept; constexpr T& operator*() & noexcept;-4- Preconditions: *this contains a value.
-5- Returns: val*val. -6- […]constexpr T&& operator*() && noexcept; constexpr const T&& operator*() const && noexcept;-7- Preconditions: *this contains a value.
-8- Effects: Equivalent to: return std::move(val*val);constexpr explicit operator bool() const noexcept;-9- Returns: true if and only if *this contains a value.
-10- Remarks: This function is a constexpr function.constexpr bool has_value() const noexcept;-11- Returns: true if and only if *this contains a value.
-12- Remarks: This function is a constexpr function.constexpr const T& value() const &; constexpr T& value() &;-13- Effects: Equivalent to:
return has_value() ? val*val: throw bad_optional_access();constexpr T&& value() &&; constexpr const T&& value() const &&;-14- Effects: Equivalent to:
return has_value() ? std::move(val*val) : throw bad_optional_access();template<class U> constexpr T value_or(U&& v) const &;-15- Mandates: […]
-16- Effects: Equivalent to:return has_value() ? val**this: static_cast<T>(std::forward<U>(v));template<class U> constexpr T value_or(U&& v) &&;-17- Mandates: […]
-18- Effects: Equivalent to:return has_value() ? std::move(val**this) : static_cast<T>(std::forward<U>(v));
Modify [optional.monadic] as indicated:
template<class F> constexpr auto and_then(F&& f) &; template<class F> constexpr auto and_then(F&& f) const &;-1- Let U be invoke_result_t<F, decltype((val)
-2- Mandates: […] -3- Effects: Equivalent to:*val)>.if (*this) { return invoke(std::forward<F>(f), val*val); } else { return remove_cvref_t<U>(); }template<class F> constexpr auto and_then(F&& f) &&; template<class F> constexpr auto and_then(F&& f) const &&;-4- Let U be invoke_result_t<F, decltype(std::move(val
-5- Mandates: […] -6- Effects: Equivalent to:*val))>.if (*this) { return invoke(std::forward<F>(f), std::move(val*val)); } else { return remove_cvref_t<U>(); }template<class F> constexpr auto transform(F&& f) &; template<class F> constexpr auto transform(F&& f) const &;-7- Let U be remove_cv_t<invoke_result_t<F, decltype((val)
-8- Mandates: U is a non-array object type other than in_place_t or nullopt_t. The declaration*val)>>.U u(invoke(std::forward<F>(f), val*val));is well-formed for some invented variable u.
[…] -9- Returns: If *this contains a value, an optional<U> object whose contained value is direct-non-list-initialized with invoke(std::forward<F>(f), val*val); otherwise, optional<U>().template<class F> constexpr auto transform(F&& f) &&; template<class F> constexpr auto transform(F&& f) const &&;-10- Let U be remove_cv_t<invoke_result_t<F, decltype(std::move(val
-11- Mandates: U is a non-array object type other than in_place_t or nullopt_t. The declaration*val))>>.U u(invoke(std::forward<F>(f), std::move(val*val)));is well-formed for some invented variable u.
[…] -12- Returns: If *this contains a value, an optional<U> object whose contained value is direct-non-list-initialized with invoke(std::forward<F>(f), std::move(val*val)); otherwise, optional<U>().
Modify [optional.mod] as indicated:
constexpr void reset() noexcept;-1- Effects: If *this contains a value, calls
-2- Postconditions: *this does not contain a value.val->val.T::~T() to destroy the contained value; otherwise no effect.
[ 2024-10-02; Jonathan tweaks proposed resolution ]
On the reflector we decided that the union member should use `remove_cv_t`, as proposed for `expected` by issue 3891. The rest of the proposed resolution is unchanged, so that edit was made in-place below, instead of as a new resolution that supersedes the old one.
[ 2024-08-21; LWG telecon ]
During telecon review it was suggested to replace [optional.optional.general] p1 and p2. On the reflector Daniel requested to keep the "additional storage" prohibition, so that will be addressed by issue 4141 instead.
[ St. Louis 2024-06-24; Jonathan provides improved wording ]
[ 2024-03-11; Reflector poll ]
Set priority to 1 after reflector poll in November 2023. Six votes for 'Tentatively Ready' but enough uncertainty to deserve discussion at a meeting.
This wording is relative to N4964 after application of the wording of LWG 3973.
Modify [optional.optional.general], class template optional synopsis, as indicated:
namespace std { template<class T> class optional { public: using value_type = T; […] private: bool has_val; // exposition only union { T val*val; // exposition only }; }; […] }
Modify [optional.optional.general] as indicated:
-2- Member has_val indicates whether an optional<T> object contains a value
When an optional<T> object contains a value, member val points to the contained value.
Modify [optional.ctor] as indicated:
[Drafting note: Normatively, this subclause doesn't require any changes, but I'm suggesting to replace phrases of the form "[…]initializes the contained value with"] by "[…]initializes val with" as we do in [expected.object.cons]. I intentionally did not add extra "and sets has_val to true/false" since those effects are already guaranteed by the postconditions]
constexpr optional(const optional& rhs);-4- Effects: If rhs contains a value, direct-non-list-initializes val
-5- Postconditions: rhs.has_value() == this->has_value(). […]the contained valuewith*rhs.val.constexpr optional(optional&& rhs) noexcept(see below);-8- Constraints: […]
-9- Effects: If rhs contains a value, direct-non-list-initializes valthe contained valuewith std::move(*rhs.val). rhs.has_value() is unchanged. -10- Postconditions: rhs.has_value() == this->has_value(). […]template<class... Args> constexpr explicit optional(in_place_t, Args&&... args);-13- Constraints: […]
-14- Effects: Direct-non-list-initializes valthe contained valuewith std::forward<Args>(args).... -15- Postconditions: *this contains a value. […]template<class U, class... Args> constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);-18- Constraints: […]
-19- Effects: Direct-non-list-initializes valthe contained valuewith il, std::forward<Args>(args).... -20- Postconditions: *this contains a value. […]template<class U = T> constexpr explicit(see below) optional(U&& v);-23- Constraints: […]
-24- Effects: Direct-non-list-initializes valthe contained valuewith std::forward<U>(v). -25- Postconditions: *this contains a value. […]template<class U> constexpr explicit(see below) optional(const optional<U>& rhs);-28- Constraints: […]
-29- Effects: If rhs contains a value, direct-non-list-initializes valthe contained valuewith*rhs.val. -30- Postconditions: rhs.has_value() == this->has_value(). […]template<class U> constexpr explicit(see below) optional(optional<U>&& rhs);-33- Constraints: […]
-34- Effects: If rhs contains a value, direct-non-list-initializes valthe contained valuewith std::move(*rhs.val). rhs.has_value() is unchanged. -35- Postconditions: rhs.has_value() == this->has_value(). […]
Modify [optional.dtor] as indicated:
constexpr ~optional();-1- Effects: If is_trivially_destructible_v<T> != true and *this contains a value, calls
val->val.T::~T().
Modify [optional.assign] as indicated:
constexpr optional<T>& operator=(nullopt_t) noexcept;-1- Effects: If *this contains a value, calls
-2- Postconditions: *this does not contain a value.val->val.T::~T() to destroy the contained value and sets has_val to false; otherwise no effect.constexpr optional<T>& operator=(const optional& rhs);-4- Effects: See Table 58.
Table 58 — optional::operator=(const optional&) effects [tab:optional.assign.copy] *this contains a value *this does not contain a value rhs contains a value assigns *rhs.val to valthe contained valuedirect-non-list-initializes val the contained valuewith*rhs.val
and sets has_val to truerhs does not contain a value destroys the contained value by calling val->val.T::~T()
and sets has_val to falseno effect -5- Postconditions: rhs.has_value() == this->has_value().
[…]constexpr optional<T>& operator=(optional&& rhs) noexcept(see below);-8- Constraints: […]
-9- Effects: See Table 59. The result of the expression rhs.has_value() remains unchanged. -10- Postconditions: rhs.has_value() == this->has_value(). -11- Returns: *this.
Table 59 — optional::operator=(optional&&) effects [tab:optional.assign.move] *this contains a value *this does not contain a value rhs contains a value assigns std::move( *rhs.val) to valthe contained valuedirect-non-list-initializes val the contained valuewith std::move(*rhs.val) and sets has_val to truerhs does not contain a value destroys the contained value by calling
val->val.T::~T()and sets has_val to falseno effect -12- Remarks: […]
-13- If any exception is thrown, the result of the expression this->has_value() remains unchanged. If an exception is thrown during the call to T's move constructor, the state of*rhs.valvalis 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*valvaland*rhs.valvalis determined by the exception safety guarantee of T's move assignment.template<class U = T> constexpr optional<T>& operator=(U&& v);-14- Constraints: […]
-15- Effects: If *this contains a value, assigns std::forward<U>(v) to valthe contained value; otherwise direct-non-list-initializes valthe contained valuewith std::forward<U>(v). -16- Postconditions: *this contains a value. -17- Returns: *this. -18- Remarks: If any exception is thrown, the result of the expression this->has_value() 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*valand v is determined by the exception safety guarantee of T's assignment.template<class U> constexpr optional<T>& operator=(const optional<U>& rhs);-19- Constraints: […]
-20- Effects: See Table 60.
Table 60 — optional::operator=(const optional<U>&) effects [tab:optional.assign.copy.templ] *this contains a value *this does not contain a value rhs contains a value assigns *rhs.val to valthe contained valuedirect-non-list-initializes val the contained valuewith*rhs.val and sets has_val to truerhs does not contain a value destroys the contained value by calling
val->val.T::~T() and sets has_val to falseno effect -21- Postconditions: rhs.has_value() == this->has_value().
-22- Returns: *this. -23- If any exception is thrown, the result of the expression this->has_value() remains unchanged. If an exception is thrown during the call to T's constructor, the state of*rhs.valvalis 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*valand*rhs.valvalis determined by the exception safety guarantee of T's assignment.template<class U> constexpr optional<T>& operator=(optional<U>&& rhs);-24- Constraints: […]
-25- Effects: See Table 61. The result of the expression rhs.has_value() remains unchanged.
Table 61 — optional::operator=(optional<U>&&) effects [tab:optional.assign.move.templ] *this contains a value *this does not contain a value rhs contains a value assigns std::move( *rhs.val) to valthe contained valuedirect-non-list-initializes val the contained valuewith
std::move(*rhs.val) and sets has_val to truerhs does not contain a value destroys the contained value by calling
val->val.T::~T() and sets has_val to falseno effect -26- Postconditions: rhs.has_value() == this->has_value().
-27- Returns: *this. -28- If any exception is thrown, the result of the expression this->has_value() remains unchanged. If an exception is thrown during the call to T's constructor, the state of*rhs.valvalis 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*valand*rhs.valvalis determined by the exception safety guarantee of T's assignment.template<class... Args> constexpr T& emplace(Args&&... args);-29- Mandates: […]
-30- Effects: Calls *this = nullopt. Then direct-non-list-initializes valthe contained valuewith std::forward<Args>(args).... -31- Postconditions: *this contains a value. -32- Returns: valA reference to the new contained value. […] -34- Remarks: If an exception is thrown during the call to T's constructor, *this does not contain a value, and the previous val*val(if any) has been destroyed.template<class U, class... Args> constexpr T& emplace(initializer_list<U> il, Args&&... args);-35- Constraints: […]
-36- Effects: Calls *this = nullopt. Then direct-non-list-initializes valthe contained valuewith il, std::forward<Args>(args).... -37- Postconditions: *this contains a value. -38- Returns: valA reference to the new contained value. […] -40- Remarks: If an exception is thrown during the call to T's constructor, *this does not contain a value, and the previous val*val(if any) has been destroyed.
Modify [optional.swap] as indicated:
constexpr void swap(optional& rhs) noexcept(see below);-1- Mandates: […]
-2- Preconditions: […] -3- Effects: See Table 62.
Table 62 — optional::swap(optional&) effects [tab:optional.swap] *this contains a value *this does not contain a value rhs contains a value calls swap(val *(*this),*rhs.val)direct-non-list-initializes val the contained value of *this
with std::move(*rhs.val), followed by rhs.val.val->T::~T();
postcondition is that *this contains a value and rhs does
not contain a valuerhs does not contain a value direct-non-list-initializes the contained value ofrhs.val
with std::move(val*(*this)), followed by val.val->T::~T();
postcondition is that *this does not contain a value and rhs
contains a valueno effect -4- Throws: […]
-5- Remarks: […] -6- If any exception is thrown, the results of the expressions this->has_value() and rhs.has_value() remain unchanged. If an exception is thrown during the call to function swap, the state of val*valand*rhs.valvalis determined by the exception safety guarantee of swap for lvalues of T. If an exception is thrown during the call to T's move constructor, the state of val*valand*rhs.valvalis determined by the exception safety guarantee of T's move constructor.
Modify [optional.observe] as indicated:
constexpr const T* operator->() const noexcept; constexpr T* operator->() noexcept;-1- Preconditions: *this contains a value.
-2- Returns: addressof(val)val. -3- […]constexpr const T& operator*() const & noexcept; constexpr T& operator*() & noexcept;-4- Preconditions: *this contains a value.
-5- Returns: val*val. -6- […]constexpr T&& operator*() && noexcept; constexpr const T&& operator*() const && noexcept;-7- Preconditions: *this contains a value.
-8- Effects: Equivalent to: return std::move(val*val);constexpr explicit operator bool() const noexcept;
-9- Returns: true if and only if *this contains a value.-10- Remarks: This function is a constexpr function.constexpr bool has_value() const noexcept;-11- Returns: has_val
-12- Remarks: These functions aretrue if and only if *this contains a value.This function is aconstexpr functions.constexpr const T& value() const &; constexpr T& value() &;-13- Effects: Equivalent to:
return has_value() ? val*val: throw bad_optional_access();constexpr T&& value() &&; constexpr const T&& value() const &&;-14- Effects: Equivalent to:
return has_value() ? std::move(val*val) : throw bad_optional_access();template<class U> constexpr T value_or(U&& v) const &;-15- Mandates: […]
-16- Effects: Equivalent to:return has_value() ? val**this: static_cast<T>(std::forward<U>(v));template<class U> constexpr T value_or(U&& v) &&;-17- Mandates: […]
-18- Effects: Equivalent to:return has_value() ? std::move(val**this) : static_cast<T>(std::forward<U>(v));
Modify [optional.monadic] as indicated:
template<class F> constexpr auto and_then(F&& f) &; template<class F> constexpr auto and_then(F&& f) const &;-1- Let U be invoke_result_t<F, decltype((val)
-2- Mandates: […] -3- Effects: Equivalent to:*val)>.if (*this) { return invoke(std::forward<F>(f), val*val); } else { return remove_cvref_t<U>(); }template<class F> constexpr auto and_then(F&& f) &&; template<class F> constexpr auto and_then(F&& f) const &&;-4- Let U be invoke_result_t<F, decltype(std::move(val
-5- Mandates: […] -6- Effects: Equivalent to:*val))>.if (*this) { return invoke(std::forward<F>(f), std::move(val*val)); } else { return remove_cvref_t<U>(); }template<class F> constexpr auto transform(F&& f) &; template<class F> constexpr auto transform(F&& f) const &;-7- Let U be remove_cv_t<invoke_result_t<F, decltype((val)
-8- Mandates: U is a non-array object type other than in_place_t or nullopt_t. The declaration*val)>>.U u(invoke(std::forward<F>(f), val*val));is well-formed for some invented variable u.
[…] -9- Returns: If *this contains a value, an optional<U> object whose contained value is direct-non-list-initialized with invoke(std::forward<F>(f), val*val); otherwise, optional<U>().template<class F> constexpr auto transform(F&& f) &&; template<class F> constexpr auto transform(F&& f) const &&;-10- Let U be remove_cv_t<invoke_result_t<F, decltype(std::move(val
-11- Mandates: U is a non-array object type other than in_place_t or nullopt_t. The declaration*val))>>.U u(invoke(std::forward<F>(f), std::move(val*val)));is well-formed for some invented variable u.
[…] -12- Returns: If *this contains a value, an optional<U> object whose contained value is direct-non-list-initialized with invoke(std::forward<F>(f), std::move(val*val)); otherwise, optional<U>().
Modify [optional.mod] as indicated:
constexpr void reset() noexcept;-1- Effects: If *this contains a value, calls
-2- Postconditions: *this does not contain a value.val->val.T::~T() to destroy the contained value and sets has_val to false; otherwise no effect.
[ 2023-11-26; Daniel provides wording ]
The proposed wording is considerably influenced by that of the specification of expected, but attempts to reduce the amount of changes to not perfectly mimic it. Although "the contained value" is a magic word of power it seemed feasible and simpler to use the new exposition-only member val directly in some (but not all) places, usually involved with initializations.
Furthermore, I have only added "and sets has_val to true/false" where either the Effects wording says "otherwise no effect" or in other cases if the postconditions did not already say that indirectly. I also added extra mentioning of has_val changes in tables where different cells had very different effects on that member (unless these cells specify postconditions), to prevent misunderstanding.
The resolution of LWG 3973 (adopted in Kona) changed all
occurrences of value()
to *val
.
The intention was not to change the meaning, just avoid the non-freestanding
value()
function, and avoid ADL that would be caused by using
**this
.
However, in the const
overloads such as
and_then(F&&) const
the type of value()
was const T&
, but the type of *val
is always
T&
. This implies that the const overloads invoke the callable
with a non-const argument, which is incorrect (and would be undefined
behaviour for a const std::optional<T>
).
On the LWG reflector it was suggested that we should rewrite the specification
of std::optional
to stop using an exposition-only data member
of type T*
. No such member ever exists in real implemetations,
so it is misleading and leads to specification bugs of this sort.
Change the class definition in [optional.optional.general]
to use a union, and update every use of val
accordingly
throughout [optional.optional].
For consistency with [expected.object.general] we might
also want to introduce a bool has_val
member and refer to
that in the specification.
private:
T *val; // exposition onlybool has_val; // exposition only union { T val; // exposition only }; };
For example, in [optional.mod]:
-1- Effects: If
*this
contains a value, callsval
to destroy the contained value and sets->.T::~T()has_val
tofalse
; otherwise no effect.
History | |||
---|---|---|---|
Date | User | Action | Args |
2024-10-02 11:35:58 | admin | set | messages: + msg14396 |
2024-08-21 19:56:50 | admin | set | messages: + msg14339 |
2024-06-24 22:47:16 | admin | set | messages: + msg14214 |
2024-06-24 22:47:16 | admin | set | status: new -> open |
2024-03-11 21:55:38 | admin | set | messages: + msg13984 |
2023-11-26 16:58:28 | admin | set | messages: + msg13880 |
2023-11-26 16:58:28 | admin | set | messages: + msg13879 |
2023-11-24 00:00:00 | admin | create |