This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
optional::value_or
return statement is inconsistent with MandatesSection: 22.5.3.7 [optional.observe], 22.5.4.6 [optional.ref.observe], 22.8.6.6 [expected.object.obs] Status: New Submitter: Hewill Kang Opened: 2025-09-06 Last modified: 2025-10-10
Priority: Not Prioritized
View other active issues in [optional.observe].
View all other issues in [optional.observe].
View all issues with New status.
Discussion:
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.
T
by static_cast
, which is not checked by
is_convertible_v
since it only checks for implicit conversions.
This results in rare cases where Mandates may not be violated, but value_or
is ill-formed
(demo):
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
.
Proposed resolution:
This wording is relative to N5014.
Modify 22.5.3.7 [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>
istrue
.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>
istrue
.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 22.5.4.6 [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:X
beremove_cv_t<T>
.is_constructible_v<X, T&> && is_convertible_v<U, X>
istrue
. -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 22.8.6.6 [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>
istrue
andis_convertible_v<U, T>
istrue
.-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>
istrue
andis_convertible_v<U, T>
istrue
.-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);