Issue 4222: expected constructor from a single value missing a constraint (original) (raw)
This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.
4222. expected constructor from a single value missing a constraint
Section: 22.8.6.2 [expected.object.cons] Status: WP Submitter: Bronek Kozicki Opened: 2025-03-12 Last modified: 2025-06-23
Priority: Not Prioritized
View all other issues in [expected.object.cons].
View all issues with WP 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> 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.
The proposed resolution is to add the following additional constraint to this constructor:
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);
[2025-06-13; Reflector poll]
Set status to Tentatively Ready after nine votes in favour during reflector poll.
[Sofia 2025-06-21; Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N5001.
- Modify 22.8.6.2 [expected.object.cons] as indicated:
template<class U = remove_cv_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
Tis cvbool,remove_cvref_t<U>is not a specialization ofexpected.
-24- Effects: Direct-non-list-initializes
_val_withstd::forward<U>(v).-25- Postconditions:
has_value()istrue.-26- Throws: Any exception thrown by the initialization of
_val_. - (23.1) —