diff --git a/xml/issue4015.xml b/xml/issue4015.xml
index 508f00c1d8..2a544868ee 100644
--- a/xml/issue4015.xml
+++ b/xml/issue4015.xml
@@ -58,11 +58,743 @@ value and sets has_val
to false
;
otherwise no effect.
+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. +
+This wording is relative to
Modify
+++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
+++-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
++ ++[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
+. 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 value+with 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 value+with 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 value+with 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
+++constexpr ~optional(); +++++-1- Effects: If is_trivially_destructible_v<T> != true and *this contains a value, +calls
+val->val.T::~T(). +
Modify
+++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 value+with*rhs.val
+and sets has_val to true ++ +rhs does not contain a value +destroys the contained value by calling +val->val.T::~T()
+and sets has_val to false +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) and sets has_val to true+ +rhs 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*valval+and*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*val+and 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 value+with*rhs.val and sets has_val to true+ +rhs 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 true+ +rhs 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
+++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 value+ +rhs 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
+++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
+++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
+++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. +