Created on 2025-09-06.00:00:00 last changed 2 weeks ago
Proposed resolution:
This wording is relative to the working draft after N5014.
Modify [optional.optional.general] as indicated:
[…] template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&&) const &; template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&&) &&; […]
Modify [optional.observe] as indicated:
template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& v) const &;-?- Let
Xberemove_cv_t<T>.-15- Mandates:
-16- Effects: Equivalent to:is `true`.is_copy_constructible_v<T>is_convertible_v<const T&, X> && is_convertible_v<U&&,TX>return has_value() ? val : static_cast<T>(std::forward<U>(v));if (has_value()) return val; return std::forward<U>(v);template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& v) &&;-?- Let
Xberemove_cv_t<T>.-17- Mandates:
-18- Effects: Equivalent to:is `true`.is_move_constructible_v<T>is_convertible_v<T, X> && is_convertible_v<U&&,TX>return has_value() ? std::move(val) : static_cast<T>(std::forward<U>(v));if (has_value()) return std::move(val); return std::forward<U>(v);
Modify [optional.ref.observe] as indicated:
constexpr T& value() const;-7- Effects: Equivalent to:
return has_value() ? *val : throw bad_optional_access();if (has_value()) return *val; throw bad_optional_access();template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& u) const;-8- Let
-9- Mandates:Xberemove_cv_t<T>.is `true`. -10- Effects: Equivalent to:is_constructible_v<T>is_convertible_v<T&, X> && is_convertible_v<U, X>return has_value() ? *val : static_cast<X>(std::forward<U>(u));if (has_value()) return *val; return std::forward<U>(u);
Modify [expected.object.general] as indicated:
[…] template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&&) const &; template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&&) &&; […]
Modify [expected.object.obs] as indicated:
template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& v) const &;-?- Let
Xberemove_cv_t<T>.-18- Mandates:
is_copy_constructible_v<T>is `true` andis_convertible_v<const T&, X> &&is_convertible_v<U, T>is `true`.-19- Returns:-?- Effects: Equivalent to:has_value() ? **this : static_cast<T>(std::forward<U>(v)).if (has_value()) return val; return std::forward<U>(v);template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& v) &&;-?- Let
Xberemove_cv_t<T>.-20- Mandates:
is_move_constructible_v<T>is `true` andis_convertible_v<T, X> &&is_convertible_v<U, T>is `true`.-21- Returns:-?- Effects: Equivalent to:has_value() ? std::move(**this) : static_cast<T>(std::forward<U>(v)).if (has_value()) return std::move(val); return std::forward<U>(v);
[ 2025-11-19; Jonathan provides updated wording ]
Incorporate proposed resolution of 3424 as well, so that the return type is never cv-qualified.
[ 2025-10-16; Reflector poll ]
Set priority to 3 after reflector poll.
This would need to use `val` instead of `**this` if LWG 4015 is accepted.
This wording is relative to N5014.
Modify [optional.observe] as indicated:
template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) const &;-15- Mandates:
-16- Effects: Equivalent to:is_copy_constructible_v<T> && is_convertible_v<U&&, T>is `true`.return has_value() ? **this : static_cast<T>(std::forward<U>(v));if (has_value()) return **this; return std::forward<U>(v);template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) &&;-17- Mandates:
-18- Effects: Equivalent to:is_move_constructible_v<T> && is_convertible_v<U&&, T>is `true`.return has_value() ? std::move(**this) : static_cast<T>(std::forward<U>(v));if (has_value()) return std::move(**this); return std::forward<U>(v);
Modify [optional.ref.observe] as indicated:
template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& u) const;-8- Let
-9- Mandates:Xberemove_cv_t<T>.is_constructible_v<X, T&> && is_convertible_v<U, X>is `true`. -10- Effects: Equivalent to:return has_value() ? *val : static_cast<X>(std::forward<U>(u));if (has_value()) return *val; return std::forward<U>(u);
Modify [expected.object.obs] as indicated:
template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) const &;-18- Mandates:
is_copy_constructible_v<T>is `true` andis_convertible_v<U, T>is `true`.-19- Returns:-?- Effects: Equivalent to:has_value() ? **this : static_cast<T>(std::forward<U>(v)).if (has_value()) return **this; return std::forward<U>(v);template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) &&;-20- Mandates:
is_move_constructible_v<T>is `true` andis_convertible_v<U, T>is `true`.-21- Returns:-?- Effects: Equivalent to:has_value() ? std::move(**this) : static_cast<T>(std::forward<U>(v)).if (has_value()) return std::move(**this); return std::forward<U>(v);
optional<T>::value_or(U&&) requires is_convertible_v<U&&, T>
to ensure that `T` can be convert from `U` when `optional` has no value.
struct S {
operator int() const;
explicit operator int() = delete;
};
int main() {
std::optional<int>{}.value_or(S{}); // fire
}
It is reasonable to create objects that stick to Mandates. The same goes for `expected::value_or`.
Daniel: This issue has considerable overlap with LWG 4281.| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2025-11-19 18:55:35 | admin | set | messages: + msg15755 |
| 2025-11-11 15:51:14 | admin | set | messages: + msg15733 |
| 2025-10-16 11:35:46 | admin | set | messages: + msg15183 |
| 2025-10-10 15:07:03 | admin | set | messages: + msg15134 |
| 2025-09-06 00:00:00 | admin | create | |