Section: 22.8.6.7 [expected.object.monadic], 22.5.3.8 [optional.monadic] Status: WP Submitter: Jiang An Opened: 2023-08-10 Last modified: 2023-11-22
Priority: Not Prioritized
View all other issues in [expected.object.monadic].
View all issues with WP status.
Discussion:
LWG 3938 switched to use **this to access the value stored in std::expected.
However, as shown in LWG 3969, **this can trigger ADL and find an unwanted overload,
and thus may caused unintended behavior.
**this, but use the name of the union member instead.
Moreover, P2407R5 will change the monadic operations of std::optional to use **this,
which is also problematic.
[2023-09-19; Wording update]
Several people preferred to replace operator*() by the corresponding union members, so this part of the proposed wording
has been adjusted, which is a rather mechanical replacement.
[2023-10-30; Reflector poll]
Set status to Tentatively Ready after five votes in favour during reflector poll.
[2023-11-11 Approved at November 2023 meeting in Kona. Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4958.
Modify 22.8.6.7 [expected.object.monadic] as indicated:
[Drafting note: Effectively replace all occurrences of
**thisbyval, except fordecltype.]
template<class F> constexpr auto and_then(F&& f) &; template<class F> constexpr auto and_then(F&& f) const &;-1- Let
-2- […] -3- […] -4- Effects: Equivalent to:Uberemove_cvref_t<invoke_result_t<F, decltype(.**this(val))>>if (has_value()) return invoke(std::forward<F>(f),**thisval); else return U(unexpect, error());template<class F> constexpr auto and_then(F&& f) &&; template<class F> constexpr auto and_then(F&& f) const &&;-5- Let
-6- […] -7- […] -8- Effects: Equivalent to:Uberemove_cvref_t<invoke_result_t<F, decltype((std::move(.**thisval))>>if (has_value()) return invoke(std::forward<F>(f), std::move(**thisval)); else return U(unexpect, std::move(error()));template<class F> constexpr auto or_else(F&& f) &; template<class F> constexpr auto or_else(F&& f) const &;-9- Let
-10- Constraints:Gberemove_cvref_t<invoke_result_t<F, decltype(error())>>.is_constructible_v<T, decltype(is**this(val))>true. -11- […] -12- Effects: Equivalent to:if (has_value()) return G(in_place,**thisval); else return invoke(std::forward<F>(f), error());template<class F> constexpr auto or_else(F&& f) &&; template<class F> constexpr auto or_else(F&& f) const &&;-13- Let
-14- Constraints:Gberemove_cvref_t<invoke_result_t<F, decltype(std::move(error()))>>.is_constructible_v<T, decltype(std::move(is**thisval))>true. -15- […] -16- Effects: Equivalent to:if (has_value()) return G(in_place, std::move(**thisval)); else return invoke(std::forward<F>(f), std::move(error()));template<class F> constexpr auto transform(F&& f) &; template<class F> constexpr auto transform(F&& f) const &;-17- Let
-18- […] -19- Mandates:Uberemove_cvref_t<invoke_result_t<F, decltype(.**this(val))>>Uis a valid value type forexpected. Ifis_void_v<U>isfalse, the declarationU u(invoke(std::forward<F>(f),**thisval));is well-formed.
-20- Effects:
(20.1) — […]
(20.2) — Otherwise, if
is_void_v<U>isfalse, returns anexpected<U, E>object whosehas_valmember istrueandvalmember is direct-non-list-initialized withinvoke(std::forward<F>(f),.**thisval)(20.3) — Otherwise, evaluates
invoke(std::forward<F>(f),and then returns**thisval)expected<U, E>().template<class F> constexpr auto transform(F&& f) &&; template<class F> constexpr auto transform(F&& f) const &&;-21- Let
-22- […] -23- Mandates:Uberemove_cvref_t<invoke_result_t<F, decltype(std::move(.**thisval))>>Uis a valid value type forexpected. Ifis_void_v<U>isfalse, the declarationU u(invoke(std::forward<F>(f), std::move(**thisval)));is well-formed.
-24- Effects:
(24.1) — […]
(24.2) — Otherwise, if
is_void_v<U>isfalse, returns anexpected<U, E>object whosehas_valmember istrueandvalmember is direct-non-list-initialized withinvoke(std::forward<F>(f), std::move(.**thisval))(24.3) — Otherwise, evaluates
invoke(std::forward<F>(f), std::move(and then returns**thisval))expected<U, E>().template<class F> constexpr auto transform_error(F&& f) &; template<class F> constexpr auto transform_error(F&& f) const &;-25- Let
-26- Constraints:Gberemove_cvref_t<invoke_result_t<F, decltype(error())>>.is_constructible_v<T, decltype(is**this(val))>true. -27- Mandates: […] -28- Returns: Ifhas_value()istrue,expected<T, G>(in_place,; otherwise, an**thisval)expected<T, G>object whosehas_valmember isfalseandunexmember is direct-non-list-initialized withinvoke(std::forward<F>(f), error()).template<class F> constexpr auto transform_error(F&& f) &&; template<class F> constexpr auto transform_error(F&& f) const &&;-29- Let
-30- Constraints:Gberemove_cvref_t<invoke_result_t<F, decltype(std::move(error()))>>.is_constructible_v<T, decltype(std::move(is**thisval))>true. -31- Mandates: […] -32- Returns: Ifhas_value()istrue,expected<T, G>(in_place, std::move(; otherwise, an**thisval))expected<T, G>object whosehas_valmember isfalseandunexmember is direct-non-list-initialized withinvoke(std::forward<F>(f), std::move(error())).
Modify 22.5.3.8 [optional.monadic] as indicated:
[Drafting note: Effectively replace all occurrences of
value()by*val.]
template<class F> constexpr auto and_then(F&& f) &; template<class F> constexpr auto and_then(F&& f) const &;-1- Let
-2- […] -3- Effects: Equivalent to:Ubeinvoke_result_t<F, decltype(.value()*val)>if (*this) { return invoke(std::forward<F>(f),value()*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
-5- […] -6- Effects: Equivalent to:Ubeinvoke_result_t<F, decltype(std::move(.value()*val))>if (*this) { return invoke(std::forward<F>(f), std::move(value()*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
-8- Mandates:Uberemove_cv_t<invoke_result_t<F, decltype(.value()*val)>>Uis a non-array object type other thanin_place_tornullopt_t. The declarationU u(invoke(std::forward<F>(f),value()*val));is well-formed for some invented variable
[…] -9- Returns: Ifu.*thiscontains a value, anoptional<U>object whose contained value is direct-non-list-initialized withinvoke(std::forward<F>(f),; otherwise,value()*val)optional<U>().template<class F> constexpr auto transform(F&& f) &&; template<class F> constexpr auto transform(F&& f) const &&;-10- Let
-11- Mandates:Uberemove_cv_t<invoke_result_t<F, decltype(std::move(.value()*val))>>Uis a non-array object type other thanin_place_tornullopt_t. The declarationU u(invoke(std::forward<F>(f), std::move(value()*val)));is well-formed for some invented variable
[…] -12- Returns: Ifu.*thiscontains a value, anoptional<U>object whose contained value is direct-non-list-initialized withinvoke(std::forward<F>(f), std::move(; otherwise,value()*val))optional<U>().