Created on 2023-08-10.00:00:00 last changed 12 months ago
Proposed resolution:
This wording is relative to N4958.
Modify [expected.object.monadic] as indicated:
[Drafting note: Effectively replace all occurrences of **this by val, except for decltype.]
template<class F> constexpr auto and_then(F&& f) &; template<class F> constexpr auto and_then(F&& f) const &;-1- Let U be remove_cvref_t<invoke_result_t<F, decltype(
-2- […] -3- […] -4- Effects: Equivalent to:**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 U be remove_cvref_t<invoke_result_t<F, decltype((std::move(
-6- […] -7- […] -8- Effects: Equivalent to:**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 G be remove_cvref_t<invoke_result_t<F, decltype(error())>>.
-10- Constraints: is_constructible_v<T, decltype(**this(val))> is 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 G be remove_cvref_t<invoke_result_t<F, decltype(std::move(error()))>>.
-14- Constraints: is_constructible_v<T, decltype(std::move(**thisval))> is 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 U be remove_cvref_t<invoke_result_t<F, decltype(
-18- […] -19- Mandates: U is a valid value type for expected. If is_void_v<U> is false, the declaration**this(val))>>.U u(invoke(std::forward<F>(f),**thisval));is well-formed.
-20- Effects:
(20.1) — […]
(20.2) — Otherwise, if is_void_v<U> is false, returns an expected<U, E> object whose has_val member is true and val member is direct-non-list-initialized with invoke(std::forward<F>(f),
**thisval).(20.3) — Otherwise, evaluates invoke(std::forward<F>(f),
**thisval) and then returns expected<U, E>().template<class F> constexpr auto transform(F&& f) &&; template<class F> constexpr auto transform(F&& f) const &&;-21- Let U be remove_cvref_t<invoke_result_t<F, decltype(std::move(
-22- […] -23- Mandates: U is a valid value type for expected. If is_void_v<U> is false, the declaration**thisval))>>.U u(invoke(std::forward<F>(f), std::move(**thisval)));is well-formed.
-24- Effects:
(24.1) — […]
(24.2) — Otherwise, if is_void_v<U> is false, returns an expected<U, E> object whose has_val member is true and val member is direct-non-list-initialized with invoke(std::forward<F>(f), std::move(
**thisval)).(24.3) — Otherwise, evaluates invoke(std::forward<F>(f), std::move(
**thisval)) and then returns expected<U, E>().template<class F> constexpr auto transform_error(F&& f) &; template<class F> constexpr auto transform_error(F&& f) const &;-25- Let G be remove_cvref_t<invoke_result_t<F, decltype(error())>>.
-26- Constraints: is_constructible_v<T, decltype(**this(val))> is true. -27- Mandates: […] -28- Returns: If has_value() is true, expected<T, G>(in_place,**thisval); otherwise, an expected<T, G> object whose has_val member is false and unex member is direct-non-list-initialized with invoke(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 G be remove_cvref_t<invoke_result_t<F, decltype(std::move(error()))>>.
-30- Constraints: is_constructible_v<T, decltype(std::move(**thisval))> is true. -31- Mandates: […] -32- Returns: If has_value() is true, expected<T, G>(in_place, std::move(**thisval)); otherwise, an expected<T, G> object whose has_val member is false and unex member is direct-non-list-initialized with invoke(std::forward<F>(f), std::move(error())).
Modify [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 U be invoke_result_t<F, decltype(
-2- […] -3- Effects: Equivalent to: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 U be invoke_result_t<F, decltype(std::move(
-5- […] -6- Effects: Equivalent to: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 U be remove_cv_t<invoke_result_t<F, decltype(
-8- Mandates: U is a non-array object type other than in_place_t or nullopt_t. The declarationvalue()*val)>>.U u(invoke(std::forward<F>(f),value()*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),value()*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(
-11- Mandates: U is a non-array object type other than in_place_t or nullopt_t. The declarationvalue()*val))>>.U u(invoke(std::forward<F>(f), std::move(value()*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(value()*val)); otherwise, optional<U>().
[ 2023-11-11 Approved at November 2023 meeting in Kona. Status changed: Voting → WP. ]
[ 2023-10-30; Reflector poll ]
Set status to Tentatively Ready after five votes in favour during reflector poll.
[ 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.
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.
Current implementations behave correctly (Godbolt link): they don't direct use **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.History | |||
---|---|---|---|
Date | User | Action | Args |
2023-11-13 14:08:10 | admin | set | messages: + msg13851 |
2023-11-13 14:08:10 | admin | set | status: voting -> wp |
2023-11-07 21:41:54 | admin | set | status: ready -> voting |
2023-10-30 17:22:20 | admin | set | messages: + msg13791 |
2023-10-30 17:22:20 | admin | set | status: new -> ready |
2023-09-19 15:37:40 | admin | set | messages: + msg13720 |
2023-09-16 14:20:14 | admin | set | messages: + msg13714 |
2023-08-10 00:00:00 | admin | create |