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.
expected
constructor from a single value missing a constraintSection: 22.8.6.2 [expected.object.cons] Status: New Submitter: Bronek Kozicki Opened: 2025-03-12 Last modified: 2025-03-15
Priority: Not Prioritized
View all other issues in [expected.object.cons].
View all issues with New status.
Discussion:
When an expected
object is initialized with a constructor taking first parameter of type unexpect_t
,
the expectation is that the object will be always initialized in disengaged state (i.e. the user expected
postcondition is that has_value()
will be false
), as in the example:
struct T { explicit T(auto) {} }; struct E { E() {} }; int main() { expected<T, E> a(unexpect); assert(!a.has_value()); }
This does not hold when both value type T
and error type E
have certain properties. Observe:
struct T { explicit T(auto) {} }; struct E { E(int) {} }; // Only this line changed from the above example int main() { expected<T, E> a(unexpect); assert(!a.has_value()); // This assert will now fail }
In the example above the overload resolution of a
finds the universal single parameter constructor for
initializing expected
in engaged state (22.8.6.2 [expected.object.cons] p23):
template<class U = remove_cv_t<T>> constexpr explicit(!is_convertible_v<U, T>) expected(U&& v);
This constructor has a list of constraints which does not mention unexpect_t
(but it mentions e.g. unexpected
and
in_place_t
). Email exchange with the author of expected
confirmed that it is an omission.
is_same_v<remove_cvref_t<U>, unexpect_t>
isfalse
This will result in the above, most likely buggy, program to become ill-formed. If the user intent was for the object
to be constructed in an engaged state, passing unexpect_t
to the T
constructor, they can fix the compilation error
like so:
expected<T, E> a(in_place, unexpect);
Proposed resolution:
This wording is relative to N5001.
Modify 22.8.6.2 [expected.object.cons] as indicated:
template<class U = remove_cv_t<T>> constexpr explicit(!is_convertible_v<U, T>) expected(U&& v);-23- Constraints:
(23.1) —
is_same_v<remove_cvref_t<U>, in_place_t>
isfalse
; and(23.2) —
is_same_v<expected, remove_cvref_t<U>>
isfalse
; and(23.?) —
is_same_v<remove_cvref_t<U>, unexpect_t>
isfalse
; and(23.3) —
remove_cvref_t<U>
is not a specialization ofunexpected
; and(23.4) —
is_constructible_v<T, U>
istrue
; and(23.5) — if
T
is cvbool
,remove_cvref_t<U>
is not a specialization ofexpected
.-24- Effects: Direct-non-list-initializes
-25- Postconditions:val
withstd::forward<U>(v)
.has_value()
istrue
. -26- Throws: Any exception thrown by the initialization ofval
.