This paper is an update to [P0323r8], with significant wording fixes provided by the most wonderful Jonathan Wakely.
Revision 7 was published a year prior to revision 8. No official LWG reviews have occurred between revision 7 and 9. As you read this paper, it has been:
-
since [N4015] was initially published.
-
since the first LEWG review.
-
since LEWG forwarded the paper to LWG for inclusion in the Library Fundamentals TS v3.
Now that C++20 is winding down, the authors hope that LWG will be able to finalize its wording review, and move the paper to the Library Fundamentals TS v3. This will help the Committee obtain feedback on a feature which should be able to make it to C++23.
Revision 7 of this paper revised [P0323r6] and [P0323r5] with feedback received from LWG. [P0323r4] contains motivation, design rationale, implementability information, sample usage, history, alternative designs and related types. Revision 5 onwards only contain wording and open questions because their purpose is twofold:
-
Present appropriate wording for inclusion in the Library Fundamentals TS v3.
-
List open questions which the TS should aim to answer.
1. Wording
Below, substitute the �
character with a number or name the editor finds
appropriate for the sub-section.
1.1. �.� Expected objects [expected]
1.2. �.�.1 In general [expected.general]
This subclause describes class template
that represents expected
objects. An
object holds an object of type
or an object of
type
and manages the lifetime of the contained objects.
1.3. �.�.2 Header < experimental / expected >
synopsis [expected.synop]
namespace std { namespace experimental { inline namespace fundamentals_v3 { // �.�.4, class template expected template < class T , class E > class expected ; // �.�.5, class template unexpected template < class E > class unexpected ; template < class E > unexpected ( E ) -> unexpected < E > ; // �.�.6, class bad_expected_access template < class E > class bad_expected_access ; // �.�.7, Specialization for void template <> class bad_expected_access < void > ; // �.�.8, unexpect tag struct unexpect_t { explicit unexpect_t () = default ; }; inline constexpr unexpect_t unexpect {}; }}}
1.4. �.�.3 Definitions [expected.defs]
1.5. �.�.4 Class template expected [expected.expected]
template < class T , class E > class expected { public : using value_type = T ; using error_type = E ; using unexpected_type = unexpected < E > ; template < class U > using rebind = expected < U , error_type > ; // �.�.4.1, constructors constexpr expected (); constexpr expected ( const expected & ); constexpr expected ( expected && ) noexcept ( see below ); template < class U , class G > explicit ( see below ) constexpr expected ( const expected < U , G >& ); template < class U , class G > explicit ( see below ) constexpr expected ( expected < U , G >&& ); template < class U = T > explicit ( see below ) constexpr expected ( U && v ); template < class G = E > constexpr expected ( const unexpected < G >& ); template < class G = E > constexpr expected ( unexpected < G >&& ); template < class ... Args > constexpr explicit expected ( in_place_t , Args && ...); template < class U , class ... Args > constexpr explicit expected ( in_place_t , initializer_list < U > , Args && ...); template < class ... Args > constexpr explicit expected ( unexpect_t , Args && ...); template < class U , class ... Args > constexpr explicit expected ( unexpect_t , initializer_list < U > , Args && ...); // �.�.4.2, destructor ~ expected (); // �.�.4.3, assignment expected & operator = ( const expected & ); expected & operator = ( expected && ) noexcept ( see below ); template < class U = T > expected & operator = ( U && ); template < class G = E > expected & operator = ( const unexpected < G >& ); template < class G = E > expected & operator = ( unexpected < G >&& ); // �.�.4.4, modifiers template < class ... Args > T & emplace ( Args && ...); template < class U , class ... Args > T & emplace ( initializer_list < U > , Args && ...); // �.�.4.5, swap void swap ( expected & ) noexcept ( see below ); // �.�.4.6, observers constexpr const T * operator -> () const ; constexpr T * operator -> (); constexpr const T & operator * () const & ; constexpr T & operator * () & ; constexpr const T && operator * () const && ; constexpr T && operator * () && ; constexpr explicit operator bool () const noexcept ; constexpr bool has_value () const noexcept ; constexpr const T & value () const & ; constexpr T & value () & ; constexpr const T && value () const && ; constexpr T && value () && ; constexpr const E & error () const & ; constexpr E & error () & ; constexpr const E && error () const && ; constexpr E && error () && ; template < class U > constexpr T value_or ( U && ) const & ; template < class U > constexpr T value_or ( U && ) && ; // �.�.4.7, Expected equality operators template < class T1 , class E1 , class T2 , class E2 > friend constexpr bool operator == ( const expected < T1 , E1 >& x , const expected < T2 , E2 >& y ); template < class T1 , class E1 , class T2 , class E2 > friend constexpr bool operator != ( const expected < T1 , E1 >& x , const expected < T2 , E2 >& y ); // �.�.4.8, Comparison with T template < class T1 , class E1 , class T2 > friend constexpr bool operator == ( const expected < T1 , E1 >& , const T2 & ); template < class T1 , class E1 , class T2 > friend constexpr bool operator == ( const T2 & , const expected < T1 , E1 >& ); template < class T1 , class E1 , class T2 > friend constexpr bool operator != ( const expected < T1 , E1 >& , const T2 & ); template < class T1 , class E1 , class T2 > friend constexpr bool operator != ( const T2 & , const expected < T1 , E1 >& ); // �.�.4.9, Comparison with unexpected<E> template < class T1 , class E1 , class E2 > friend constexpr bool operator == ( const expected < T1 , E1 >& , const unexpected < E2 >& ); template < class T1 , class E1 , class E2 > friend constexpr bool operator == ( const unexpected < E2 >& , const expected < T1 , E1 >& ); template < class T1 , class E1 , class E2 > friend constexpr bool operator != ( const expected < T1 , E1 >& , const unexpected < E2 >& ); template < class T1 , class E1 , class E2 > friend constexpr bool operator != ( const unexpected < E2 >& , const expected < T1 , E1 >& ); // �.�.4.10, Specialized algorithms template < class T1 , class E1 > friend void swap ( expected < T1 , E1 >& , expected < T1 , E1 >& ) noexcept ( see below ); private : bool has_val ; // exposition only union { value_type val ; // exposition only unexpected_type unex ; // exposition only }; };
Any object of
either contains a value of type
or a value of
type
within its own storage. Implementations are not permitted
to use additional storage, such as dynamic memory, to allocate the object of
type
or the object of type
. These objects are allocated
in a region of the
storage suitably aligned for the types
and
. Members
,
and
are provided for
exposition only.
indicates whether the
object
contains an object of type
.
A program that instantiates the definition of template
for a
reference type, a function type, or for possibly cv-qualified types
,
or
for the
parameter is
ill-formed. A program that instantiates the definition of template
with the
parameter that is not a valid template parameter
for
is ill-formed.
When
is not cv
, it shall meet the requirements of
(Table 27).
shall meet the requirements of
(Table 27).
1.6. �.�.4.1 Constructors [expected.object.ctor]
constexpr expected ();
Constraints:
is true
or
is cv
.
Effects: Value-initializes
if
is not cv
.
Ensures:
is true
.
Throws: Any exception thrown by the selected constructor of
.
Remarks: If value-initialization of
is a constant subexpression or
is cv
this constructor is constexpr.
constexpr expected ( const expected & rhs );
Effects: If
and
is not cv
, initializes
as if
direct-non-list-initializing an object of type
with the expression
.
Otherwise, initializes
as if direct-non-list-initializing an object
of type
with the expression
.
Ensures:
.
Throws: Any exception thrown by the selected constructor of
or
.
Remarks: This constructor is defined as deleted unless:
-
isis_copy_constructible_v < T > true
or
is cvT
; andvoid -
isis_copy_constructible_v < E > true
.
This constructor is a constexpr constructor if:
-
isis_trivially_copy_constructible_v < T > true
or
is cvT
; andvoid -
isis_trivially_copy_constructible_v < E > true
.
constexpr expected ( expected && rhs ) noexcept ( see below );
Constraints:
-
isis_move_constructible_v < T > true
; and -
isis_move_constructible_v < E > true
.
Effects: If
and
is not cv
, initializes
as if
direct-non-list-initializing an object of type
with the expression
.
Otherwise, initializes
as if direct-non-list-initializing an object of
type
with the expression
.
Ensures:
is unchanged,
.
Throws: Any exception thrown by the selected constructor of
or
.
Remarks: The expression inside
is equivalent to:
-
isis_nothrow_move_constructible_v < T > true
or
is cvT
; andvoid -
isis_nothrow_move_constructible_v < E > true
.
Remarks: This constructor is a constexpr constructor if:
-
isis_trivially_move_constructible_v < T > true
or
is cvT
; andvoid -
isis_trivially_move_constructible_v < E > true
.
template < class U , class G > explicit ( see below ) constexpr expected ( const expected < U , G >& rhs );
Constraints:
and
are cv
or:
-
isis_constructible_v < T , const U &> true
; and -
isis_constructible_v < T , expected < U , G >&> false
; and -
isis_constructible_v < T , expected < U , G >&&> false
; and -
isis_constructible_v < T , const expected < U , G >&> false
; and -
isis_constructible_v < T , const expected < U , G >&&> false
; and -
isis_convertible_v < expected < U , G >& , T > false
; and -
isis_convertible_v < expected < U , G >&& , T > false
; and -
isis_convertible_v < const expected < U , G >& , T > false
; and -
isis_convertible_v < const expected < U , G >&& , T > false
; and -
isis_constructible_v < E , const G &> true
; and -
isis_constructible_v < unexpected < E > , expected < U , G >&> false
; and -
isis_constructible_v < unexpected < E > , expected < U , G >&&> false
; and -
isis_constructible_v < unexpected < E > , const expected < U , G >&> false
; and -
isis_constructible_v < unexpected < E > , const expected < U , G >&&> false
; and -
isis_convertible_v < expected < U , G >& , unexpected < E >> false
; and -
isis_convertible_v < expected < U , G >&& , unexpected < E >> false
; and -
isis_convertible_v < const expected < U , G >& , unexpected < E >> false
; and -
isis_convertible_v < const expected < U , G >&& , unexpected < E >> false
.
Effects: If
and
is not cv
, initializes
as if
direct-non-list-initializing an object of type
with the expression
.
Otherwise, initializes
as if direct-non-list-initializing an object
of type
with the expression
.
Ensures:
.
Throws: Any exception thrown by the selected constructor of
or
.
Remarks:
The expression inside
is true
if and only if:
-
andT
are not cvU
andvoid
isis_convertible_v < const U & , T > false
or -
isis_convertible_v < const G & , E > false
.
template < class U , class G > explicit ( see below ) constexpr expected ( expected < U , G >&& rhs );
Constraints:
and
are cv
or:
-
isis_constructible_v < T , U &&> true
; and -
isis_constructible_v < T , expected < U , G >&> false
; and -
isis_constructible_v < T , expected < U , G >&&> false
; and -
isis_constructible_v < T , const expected < U , G >&> false
; and -
isis_constructible_v < T , const expected < U , G >&&> false
; and -
isis_convertible_v < expected < U , G >& , T > false
; and -
isis_convertible_v < expected < U , G >&& , T > false
; and -
isis_convertible_v < const expected < U , G >& , T > false
; and -
isis_convertible_v < const expected < U , G >&& , T > false
; and -
isis_constructible_v < E , G &&> true
; and -
isis_constructible_v < unexpected < E > , expected < U , G >&> false
; and -
isis_constructible_v < unexpected < E > , expected < U , G >&&> false
; and -
isis_constructible_v < unexpected < E > , const expected < U , G >&> false
; and -
isis_constructible_v < unexpected < E > , const expected < U , G >&&> false
; and -
isis_convertible_v < expected < U , G >& , unexpected < E >> false
; and -
isis_convertible_v < expected < U , G >&& , unexpected < E >> false
; and -
isis_convertible_v < const expected < U , G >& , unexpected < E >> false
; and -
isis_convertible_v < const expected < U , G >&& , unexpected < E >> false
.
Effects: If
initializes
as if
direct-non-list-initializing an object of type
with the expression
or nothing if
is cv
.
Otherwise, initializes
as if direct-non-list-initializing an object
of type
with the expression
.
Ensures:
is unchanged,
.
Throws: Any exception thrown by operations specified in the effect clause.
Remarks: The expression inside
is true
if and only if
-
andT
are not cvU
andvoid
isis_convertible_v < U && , T > false
or -
isis_convertible_v < G && , E > false
.
template < class U = T > explicit ( see below ) constexpr expected ( U && v );
Constraints:
-
is not cvT
; andvoid -
isis_constructible_v < T , U &&> true
; and -
isis_same_v < remove_cvref_t < U > , in_place_t > false
; and -
isis_same_v < expected < T , E > , remove_cvref_t < U >> false
; and -
isis_same_v < unexpected < E > , remove_cvref_t < U >> false
.
Effects: Initializes
as if direct-non-list-initializing an
object of type
with the expression
.
Ensures:
is true
.
Throws: Any exception thrown by the selected constructor of
.
Remarks: If
's selected constructor is a constant subexpression, this
constructor is a constexpr constructor.
Remarks: The expression inside
is equivalent to
.
template < class G = E > explicit ( see below ) constexpr expected ( const unexpected < G >& e );
Constraints:
is true
.
Effects: Initializes
as if direct-non-list-initializing
an object of type
with the expression
.
Ensures:
is false
.
Throws: Any exception thrown by the selected constructor of
.
Remarks: If
's selected constructor is a constant
subexpression, this constructor shall be a constexpr constructor.
The expression inside
is equivalent to
.
template < class G = E > explicit ( see below ) constexpr expected ( unexpected < G >&& e );
Constraints:
is true
.
Effects: Initializes
as if direct-non-list-initializing
an object of type
with the expression
.
Ensures:
is false
.
Throws: Any exception thrown by the selected constructor of
.
Remarks: If
's selected constructor is a constant
subexpression, this constructor is a constexpr constructor. The expression
inside
is equivalent to:
is true
.
The expression inside
is equivalent to
.
template < class ... Args > constexpr explicit expected ( in_place_t , Args && ... args );
Constraints:
-
is cvT
andvoid
; orsizeof ...( Args ) == 0 -
is not cvT
andvoid
isis_constructible_v < T , Args ... > true
.
Effects: If
is cv
, no effects. Otherwise, initializes
as if
direct-non-list-initializing an object of type
with the arguments
.
Ensures:
is true
.
Throws: Any exception thrown by the selected constructor of
.
Remarks: If
's constructor selected for the initialization is a constant subexpression,
this constructor is a constexpr constructor.
template < class U , class ... Args > constexpr explicit expected ( in_place_t , initializer_list < U > il , Args && ... args );
Constraints:
is not cv
and
is true
.
Effects: Initializes
as if direct-non-list-initializing an
object of type
with the arguments
.
Ensures:
is true
.
Throws: Any exception thrown by the selected constructor of
.
Remarks: If
's constructor selected for the initialization is a constant subexpression,
this constructor is a constexpr constructor.
template < class ... Args > constexpr explicit expected ( unexpect_t , Args && ... args );
Constraints:
is true
.
Effects: Initializes
as if direct-non-list-initializing
an object of type
with the arguments
.
Ensures:
is false
.
Throws: Any exception thrown by the selected constructor of
.
Remarks: If
's constructor selected for the initialization is a
constant subexpression, this constructor is a constexpr constructor.
template < class U , class ... Args > constexpr explicit expected ( unexpect_t , initializer_list < U > il , Args && ... args );
Constraints:
is true
.
Effects: Initializes
as if direct-non-list-initializing
an object of type
with the arguments
.
Ensures:
is false
.
Throws: Any exception thrown by the selected constructor of
.
Remarks: If
's constructor selected for the initialization is a
constant subexpression, this constructor is a constexpr constructor.
1.7. �.�.4.2 Destructor [expected.object.dtor]
~ expected ();
Effects: If
is not cv
and
is false
and
, calls
. If
is false
and
, calls
.
Remarks: If either
is cv
or
is true
, and
is true
, then this destructor
is a trivial destructor.
1.8. �.�.4.3 Assignment [expected.object.assign]
expected & operator = ( const expected & rhs ) noexcept ( see below );
Effects: See Table editor-please-pick-a-number-0
|
| |
| assigns to if is not cv
|
|
|
| assigns to
|
Returns:
.
Ensures:
.
Throws: Any exception thrown by the operations specified in the effect clause.
Remarks: If any exception is thrown,
and
remain unchanged.
If an exception is thrown during the call to
's or
's copy
constructor, no effect. If an exception is thrown during the call to
's or
's copy assignment, the state of its contained value is as defined
by the exception safety guarantee of
's or
's copy assignment.
This operator is defined as deleted unless:
-
is cvT
andvoid
isis_copy_assignable_v < E > true
and
isis_copy_constructible_v < E > true
; or -
is not cvT
andvoid
isis_copy_assignable_v < T > true
and
isis_copy_constructible_v < T > true
and
isis_copy_assignable_v < E > true
and
isis_copy_constructible_v < E > true
and (
isis_nothrow_move_constructible_v < E > true
or
isis_nothrow_move_constructible_v < T > true
).
expected & operator = ( expected && rhs ) noexcept ( see below );
Effects: See Table editor-please-pick-a-number-1
|
| |
| move assign to if is not cv
|
|
|
| move assign to
|
Returns:
.
Ensures:
.
Remarks: The expression inside noexcept is equivalent to:
is true
and
is true
.
If any exception is thrown,
and
remain
unchanged. If an exception is thrown during the call to
's copy constructor,
no effect. If an exception is thrown during the call to
's copy assignment,
the state of its contained value is as defined by the exception safety guarantee
of
's copy assignment. If an exception is thrown during the call to
's
copy assignment, the state of its contained
is as defined by
the exception safety guarantee of
's copy assignment.
This operator is defined as deleted unless:
-
is cvT
andvoid
isis_nothrow_move_constructible_v < E > true
and
isis_nothrow_move_assignable_v < E > true
; or -
is not cvT
andvoid
isis_move_constructible_v < T > true
and
isis_move_assignable_v < T > true
and
isis_nothrow_move_constructible_v < E > true
and
isis_nothrow_move_assignable_v < E > true
.
template < class U = T > expected < T , E >& operator = ( U && v );
Constraints:
-
isis_void_v < T > false
; and -
isis_same_v < expected < T , E > , remove_cvref_t < U >> false
; and -
isconjunction_v < is_scalar < T > , is_same < T , decay_t < U >>> false
; and -
isis_constructible_v < T , U > true
; and -
isis_assignable_v < T & , U > true
; and -
isis_nothrow_move_constructible_v < E > true
.
Effects: See Table editor-please-pick-a-number-2
|
|
If , assigns to
|
if
otherwise
|
Returns:
.
Ensures:
is true
.
Remarks: If any exception is thrown,
remains
unchanged. If an exception is thrown during the call to
's constructor, no
effect. If an exception is thrown during the call to
's copy assignment, the
state of its contained value is as defined by the exception safety guarantee of
's copy assignment.
template < class G = E > expected < T , E >& operator = ( const unexpected < G >& e );
Constraints:
is true
and
is true
.
Effects: See Table editor-please-pick-a-number-3
|
|
assigns to
|
|
Returns:
.
Ensures:
is false
.
Remarks: If any exception is thrown,
remains unchanged.
expected < T , E >& operator = ( unexpected < G >&& e );
Effects: See Table editor-please-pick-a-number-4
|
|
| move assign to
|
Constraints:
is true
and
is true
.
Returns:
.
Ensures:
is false
.
Remarks: If any exception is thrown,
remains unchanged.
void expected < void , E >:: emplace ();
Effects:
If
-
destroys
by callingunex
,unexpect . ~ unexpected < E > () -
set
tohas_val true
Ensures:
is true
.
Throws: Nothing
template < class ... Args > T & emplace ( Args && ... args );
Constraints:
is not cv
and
is true
.
Effects:
If
, assigns
as if
constructing an object of type
with the arguments
otherwise if
is true
-
destroys
by callingunex
,unexpect . ~ unexpected < E > () -
initializes
as if direct-non-list-initializing an object of typeval
withT
andstd :: forward < Args > ( args )... -
set
tohas_val true
;
otherwise if
is true
-
constructs a
fromT tmp
(which can throw),std :: forward < Args > ( args )... -
destroys
by callingunex
,unexpect . ~ unexpected < E > () -
initializes
as if direct-non-list-initializing an object of typeval
withT
(which cannot throw) andstd :: move ( tmp ) -
set
tohas_val true
;
otherwise
-
move constructs an
fromunexpected < E > tmp
,unexpected ( this -> error ()) -
destroys
by callingunex
,unexpect . ~ unexpected < E > () -
initializes
as if direct-non-list-initializing an object of typeval
withT
. Either,std :: forward < Args > ( args )... -
the constructor didn’t throw, set
tohas_val true
, or -
the constructor did throw, so move-construct the
fromunexpected < E >
back into the expected storage (which can’t throw astmp
is nothrow-move-constructible), and re-throw the exception.E
-
Ensures:
is true
.
Returns: A reference to the new contained value
.
Throws: Any exception thrown by the operations specified in the effect clause.
Remarks: If an exception is thrown during the call to
's assignment, nothing
changes.
template < class U , class ... Args > T & emplace ( initializer_list < U > il , Args && ... args );
Constraints:
is not cv
and
is true
.
Effects:
If
, assigns
as if
constructing an object of type
with the arguments
otherwise if
is true
-
destroys
by callingunex
,unexpect . ~ unexpected < E > () -
initializes
as if direct-non-list-initializing an object of typeval
withT
andil , std :: forward < Args > ( args )... -
set
tohas_val true
;
otherwise if
is true
-
constructs a
fromT tmp
(which can throw),il , std :: forward < Args > ( args )... -
destroys
by callingunex
,unexpect . ~ unexpected < E > () -
initializes
as if direct-non-list-initializing an object of typeval
withT
(which cannot throw) andstd :: move ( tmp ) -
set
tohas_val true
;
otherwise
-
move constructs an
fromunexpected < E > tmp
,unexpected ( this -> error ()) -
destroys
by callingunex
,unexpect . ~ unexpected < E > () -
initializes
as if direct-non-list-initializing an object of typeval
withT
. Either,il , std :: forward < Args > ( args )... -
the constructor didn’t throw, set
tohas_val true
, or -
the constructor did throw, so move-construct the
fromunexpected < E >
back into the expected storage (which can’t throw astmp
is nothrow-move-constructible), and re-throw the exception.E
-
Ensures:
is true
.
Returns: A reference to the new contained value
.
Throws: Any exception thrown by the operations specified in the effect clause.
Remarks: If an exception is thrown during the call to
's assignment nothing
changes.
1.9. �.�.4.4 Swap [expected.object.swap]
void swap ( expected < T , E >& rhs ) noexcept ( see below );
Constraints:
-
Lvalues of type
areT
; andSwappable -
Lvalues of type
areE
; andSwappable -
isis_void_v < T > true
or
isis_void_v < T > false
and either:-
isis_move_constructible_v < T > true
; or -
isis_move_constructible_v < E > true
.
-
Effects: See Table editor-please-pick-a-number-5
|
| |
| if is not cv calls
| calls
|
|
| calls
|
Throws: Any exceptions that the expressions in the Effects clause throw.
Remarks: The expression inside noexcept is equivalent to:
is true
and
is true
and
is true
and
is true
.
1.10. �.�.4.5 Observers [expected.object.observe]
constexpr const T * operator -> () const ; T * operator -> ();
Constraints:
is not cv
.
Expects:
is true
.
Returns:
.
constexpr const T & operator * () const & ; T & operator * () & ;
Constraints:
is not cv
.
Expects:
is true
.
Returns:
.
constexpr T && operator * () && ; constexpr const T && operator * () const && ;
Constraints:
is not cv
.
Expects:
is true
.
Returns:
.
constexpr explicit operator bool () noexcept ;
Returns:
.
constexpr bool has_value () const noexcept ;
Returns:
.
constexpr void expected < void , E >:: value () const ;
Throws:
if
.
constexpr const T & expected :: value () const & ; constexpr T & expected :: value () & ;
Constraints:
is not cv
.
Returns:
, if
.
Throws:
if
.
constexpr T && expected :: value () && ; constexpr const T && expected :: value () const && ;
Constraints:
is not cv
.
Returns:
, if
.
Throws:
if
.
constexpr const E & error () const & ; constexpr E & error () & ;
Expects:
is false
.
Returns:
.
constexpr E && error () && ; constexpr const E && error () const && ;
Expects:
is false
.
Returns:
.
template < class U > constexpr T value_or ( U && v ) const & ;
Returns:
.
Remarks: If
is true
and
is false
the program is ill-formed.
template < class U > constexpr T value_or ( U && v ) && ;
Returns:
.
Remarks: If
is true
and
is false
the program is ill-formed.
1.11. �.�.4.6 Expected Equality operators [expected.equality_op]
template < class T1 , class E1 , class T2 , class E2 > friend constexpr bool operator == ( const expected < T1 , E1 >& x , const expected < T2 , E2 >& y );
Expects: The expressions
and
are well-formed and their results
are convertible to
.
Returns: If
, false
; otherwise if
,
; otherwise true
if
and
are cv
or
otherwise.
Remarks: Specializations of this function template, for which
and
are cv
or
and
is a core constant expression, are
constexpr functions.
template < class T1 , class E1 , class T2 , class E2 > constexpr bool operator != ( const expected < T1 , E1 >& x , const expected < T2 , E2 >& y );
Mandates: The expressions
and
are
well-formed and their results are convertible to
.
Returns: If
, true
; otherwise if
,
; otherwise true
if
and
are cv
or
.
Remarks: Specializations of this function template, for which
and
are cv
or
and
is a core
constant expression, are constexpr functions.
1.12. �.�.4.7 Comparison with T
[expected.comparison_T]
template < class T1 , class E1 , class T2 > constexpr bool operator == ( const expected < T1 , E1 >& x , const T2 & v ); template < class T1 , class E1 , class T2 > constexpr bool operator == ( const T2 & v , const expected < T1 , E1 >& x );
Mandates:
and
are not cv
and the expression
is well-formed
and its result is convertible to
. [ Note:
need not be EqualityComparable. - end note]
Returns:
.
template < class T1 , class E1 , class T2 > constexpr bool operator != ( const expected < T1 , E1 >& x , const T2 & v ); template < class T1 , class E1 , class T2 > constexpr bool operator != ( const T2 & v , const expected < T1 , E1 >& x );
Mandates:
and
are not cv
and the expression
is well-formed
and its result is convertible to
. [ Note:
need not be EqualityComparable. - end note]
Returns:
.
1.13. �.�.4.8 Comparison with unexpected < E >
[expected.comparison_unexpected_E]
template < class T1 , class E1 , class E2 > constexpr bool operator == ( const expected < T1 , E1 >& x , const unexpected < E2 >& e ); template < class T1 , class E1 , class E2 > constexpr bool operator == ( const unexpected < E2 >& e , const expected < T1 , E1 >& x );
Mandates: The expression
is well-formed and
its result is convertible to
.
Returns:
.
template < class T1 , class E1 , class E2 > constexpr bool operator != ( const expected < T1 , E1 >& x , const unexpected < E2 >& e ); template < class T1 , class E1 , class E2 > constexpr bool operator != ( const unexpected < E2 >& e , const expected < T1 , E1 >& x );
Mandates: The expression
is well-formed and
its result is convertible to
.
Returns:
.
1.14. �.�.4.9 Specialized algorithms [expected.specalg]
template < class T1 , class E1 > void swap ( expected < T1 , E1 >& x , expected < T1 , E1 >& y ) noexcept ( noexcept ( x . swap ( y )));
Constraints:
-
is cvT1
orvoid
isis_move_constructible_v < T1 > true
; and -
isis_swappable_v < T1 > true
; and -
isis_move_constructible_v < E1 > true
; and -
isis_swappable_v < E1 > true
.
Effects: Calls
.
1.15. �.�.5 Unexpected objects [expected.unexpected]
1.16. �.�.5.1 General [expected.unexpected.general]
This subclause describes class template
that
represents unexpected objects.
1.17. �.�.5.2 Class template unexpected
[expected.unexpected.object]
template < class E > class unexpected { public : constexpr unexpected ( const unexpected & ) = default ; constexpr unexpected ( unexpected && ) = default ; template < class ... Args > constexpr explicit unexpected ( in_place_t , Args && ...); template < class U , class ... Args > constexpr explicit unexpected ( in_place_t , initializer_list < U > , Args && ...); template < class Err = E > constexpr explicit unexpected ( Err && ); template < class Err > constexpr explicit ( see below ) unexpected ( const unexpected < Err >& ); template < class Err > constexpr explicit ( see below ) unexpected ( unexpected < Err >&& ); constexpr unexpected & operator = ( const unexpected & ) = default ; constexpr unexpected & operator = ( unexpected && ) = default ; template < class Err = E > constexpr unexpected & operator = ( const unexpected < Err >& ); template < class Err = E > constexpr unexpected & operator = ( unexpected < Err >&& ); constexpr const E & value () const & noexcept ; constexpr E & value () & noexcept ; constexpr const E && value () const && noexcept ; constexpr E && value () && noexcept ; void swap ( unexpected & other ) noexcept ( see below ); template < class E1 , class E2 > friend constexpr bool operator == ( const unexpected < E1 >& , const unexpected < E2 >& ); template < class E1 , class E2 > constexpr bool operator != ( const unexpected < E1 >& , const unexpected < E2 >& ); template < class E1 > friend void swap ( unexpected < E1 >& x , unexpected < E1 >& y ) noexcept ( noexcept ( x . swap ( y ))); private : E val ; // exposition only }; template < class E > unexpected ( E ) -> unexpected < E > ;
A program that instantiates the definition of
for a non-object
type, an array type, a specialization of
or an cv-qualified type is
ill-formed.
1.17.1. �.�.5.2.1 Constructors [expected.unexpected.ctor]
template < class Err > constexpr explicit unexpected ( Err && e );
Constraints:
-
isis_constructible_v < E , Err > true
; and -
isis_same_v < remove_cvref_t < U > , in_place_t > false
; and -
isis_same_v < remove_cvref_t < U > , unexpected > false
.
Effects: Initializes
as if direct-non-list-initializing an
object of type
with the expression
.
Throws: Any exception thrown by the selected constructor of
.
Remarks: If
’s selected constructor is a constant subexpression, this
constructor is a constexpr constructor.
template < class ... Args > constexpr explicit unexpected ( in_place_t , Args && ...);
Constraints:
is true
.
Effects: Initializes
as if direct-non-list-initializing an
object of type
with the arguments
.
Throws: Any exception thrown by the selected constructor of
.
Remarks:
If
’s selected constructor is a constant subexpression, this constructor is a constexpr constructor.
template < class U , class ... Args > constexpr explicit unexpected ( in_place_t , initializer_list < U > , Args && ...);
Constraints:
is true
.
Effects: Initializes
as if direct-non-list-initializing an
object of type
with the arguments
.
Throws: Any exception thrown by the selected constructor of
.
Remarks:
If
’s selected constructor is a constant subexpression, this constructor is a constexpr constructor.
template < class Err > constexpr explicit ( see below ) unexpected ( const unexpected < Err >& e );
Constraints:
-
isis_constructible_v < E , const Err &> true
; and -
isis_constructible_v < E , unexpected < Err >&> false
; and -
isis_constructible_v < E , unexpected < Err >> false
; and -
isis_constructible_v < E , const unexpected < Err >&> false
; and -
isis_constructible_v < E , const unexpected < Err >> false
; and -
isis_convertible_v < unexpected < Err >& , E > false
; and -
isis_convertible_v < unexpected < Err > , E > false
; and -
isis_convertible_v < const unexpected < Err >& , E > false
; and -
isis_convertible_v < const unexpected < Err > , E > false
.
Effects: Initializes
as if direct-non-list-initializing an
object of type
with the expression
.
Throws: Any exception thrown by the selected constructor of
.
Remarks: The expression inside
is equivalent to
.
template < class Err > constexpr explicit ( see below ) unexpected ( unexpected < Err >&& e );
Constraints:
-
isis_constructible_v < E , Err > true
; and -
isis_constructible_v < E , unexpected < Err >&> false
; and -
isis_constructible_v < E , unexpected < Err >> false
; and -
isis_constructible_v < E , const unexpected < Err >&> false
; and -
isis_constructible_v < E , const unexpected < Err >> false
; and -
isis_convertible_v < unexpected < Err >& , E > false
; and -
isis_convertible_v < unexpected < Err > , E > false
; and -
isis_convertible_v < const unexpected < Err >& , E > false
; and -
isis_convertible_v < const unexpected < Err > , E > false
.
Effects: Initializes
as if direct-non-list-initializing an
object of type
with the expression
.
Throws: Any exception thrown by the selected constructor of
.
Remarks: The expression inside
is equivalent to
.
1.17.2. �.�.5.2.2 Assignment [expected.unexpected.assign]
template < class Err = E > constexpr unexpected & operator = ( const unexpected < Err >& e );
Constraints:
is true
.
Effects: Equivalent to
.
Returns:
.
Remarks: If
’s selected assignment operator is a constant subexpression,
this function is a constexpr function.
template < class Err = E > constexpr unexpected & operator = ( unexpected < Err >&& e );
Constraints:
is true
.
Effects: Equivalent to
.
Returns:
.
Remarks: If
’s selected assignment operator is a constant subexpression,
this function is a constexpr function.
1.17.3. �.�.5.2.3 Observers [expected.unexpected.observe]
constexpr const E & value () const & ; constexpr E & value () & ;
Returns:
.
constexpr E && value () && ; constexpr const E && value () const && ;
Returns:
.
1.17.4. �.�.5.2.4 Swap [expected.unexpected.ctor]
void swap ( unexpected & other ) noexcept ( see below );
Mandates:
is true
.
Effects: Equivalent to
.
Throws: Any exceptions thrown by the operations in the relevant part of [expected.swap].
Remarks: The expression inside
is equivalent to:
is true
.
1.18. �.�.5.2.5 Equality operators [expected.unexpected.equality_op]
template < class E1 , class E2 > friend constexpr bool operator == ( const unexpected < E1 >& x , const unexpected < E2 >& y );
Returns:
.
template < class E1 , class E2 > friend constexpr bool operator != ( const unexpected < E1 >& x , const unexpected < E2 >& y );
Returns:
.
1.18.1. �.�.5.2.5 Specialized algorithms [expected.unexpected.specalg]
template < class E1 > friend void swap ( unexpected < E1 >& x , unexpected < E1 >& y ) noexcept ( noexcept ( x . swap ( y )));
Constraints:
is true
.
Effects: Equivalent to
.
1.19. �.�.6 Template Class bad_expected_access
[expected.bad_expected_access]
template < class E > class bad_expected_access : public bad_expected_access < void > { public : explicit bad_expected_access ( E ); virtual const char * what () const noexcept override ; E & error () & ; const E & error () const & ; E && error () && ; const E && error () const && ; private : E val ; // exposition only };
Wondering if we just need a
overload as we do for
.
The template class
defines the type of objects thrown as
exceptions to report the situation where an attempt is made to access the value
of
object that contains an
.
bad_expected_access :: bad_expected_access ( E e );
Effects: Initializes
with
.
Ensures:
returns an implementation-defined NTBS.
const E & error () const & ; E & error () & ;
Returns:
E && error () && ; const E && error () const && ;
Returns:
virtual const char * what () const noexcept override ;
Returns: An implementation-defined NTBS.
1.20. �.�.7 Class bad_expected_access < void >
[expected.bad_expected_access_base]
template <> class bad_expected_access < void > : public exception { public : explicit bad_expected_access (); };
1.21. �.�.8 unexpect
tag [expected.unexpect]
struct unexpect_t ( see below );
inline constexpr unexpect_t unexpect ( unspecified );
Type
does not have a default constructor or an initializer-list
constructor, and is not an aggregate.
2. Open Questions
is a vocabulary type with an opinionated design and a proven
record under varied forms in a multitude of codebases. Its current form has
undergone multiple revisions and received substantial feedback, falling roughly
in the following categories:
-
Ergonomics: is this the right way to expose such functionality?
-
Disappointment: should we expose this in the Standard, given C++'s existing error handling mechanisms?
-
STL usage: should the Standard Template Library adopt this class, at which pace, and where?
LEWG and EWG have nonetheless reached consensus that a class of this general approach is probably desirable, and the only way to truly answer these questions is to try it out in a TS and ask for explicit feedback from developers. The authors hope that developers will provide new information which they’ll be able to communicate to the Committee.
Here are open questions, and questions which the Committee thinks are settled and which new information can justify revisiting.
2.1. Ergonomics
-
Name:
-
Is
the right name?expected -
Does it express intent both as a consumer and a producer?
-
-
Is
a salient property ofE
?expected -
Is
clear on what it expresses as a return type?expected < void , E > -
Would it make sense for
to support containing bothexpected
andT
(in some designs, either one of them being optional), or is this use case better handled by a separate proposal?E -
Is the order of parameters
appropriate?< T , E > -
Is usage of
"viral" in a codebase, or can it be adopted incrementally?expected -
-
Missing
forexpected < T , E >:: emplace
.unexpected < E > -
The in-place construction from a
using theunexpected < E >
tag doesn’t conveys the in-place nature of this constructor.unexpet -
Do we need in-place using indexes, as variant?
-
-
Comparisons:
-
Are
and==
useful?!= -
Should other comparisons be provided?
-
What usages of
mandate putting instances in aexpected
, or other such container?map -
Should
be provided?hash -
What usages of
mandate putting instances in anexpected
, or other such container?unordered_map -
Should
always be comparable ifexpected < T , E >
is comparable, even ifT
is not comparable?E
-
-
Error type
:E -
Have
andexpected < T , void >
a sense?unexpected < void > -
template parameter has no default. Should it?E -
Should
be specialized for particularexpected
types such asE
and how?exception_ptr -
Should
handleexpected
types with a built-in "success" value any differently and how?E -
is not implicitly constructible from anexpected
, even when unambiguous fromE
, because as a vocabulary type it wants unexpected error construction to be verbose, and require hopping through anT
. Is the verbosity extraneous? We could alleviate the verbosity by adding aunexpected
method to. unexpected ()
, so that one doesn’t have to writeexpected
when constructing a newunexpected ( e . error ())
from an error.expected
-
-
Does usage of this class cause a meaningful performance impact compared to using error codes?
-
The accessor design offers a terse unchecked dereference operator (expected to be used alongside the implicit
conversion), as well asbool
andvalue ()
accessors which are checked. Is that a gotcha, or is it similar enough to classes such aserror ()
to be unsurprising?optional -
Is
the right thing to throw?bad_expected_access -
Should some members be
?nodiscard -
Should constructors of
disallowexpected
which are specializations ofT
?expected -
Similarly, can
andT
be abominable functions?E
2.2. Disappointment
C++ already supports exceptions and error codes,
would be a third
kind of error handling.
-
where does
work better than either exceptions or error handling?expected -
was designed to be particularly well suited to APIs which require their immediate caller to consider an error scenario. Do it succeed in that purpose?expected -
Do codebases successfully compose these three types of error handling?
-
Is debuggability any harder?
-
Is it easy to teach C++ as a whole with a third type of error handling?
2.3. STL Usage
-
Should
be used in the STL at the same time as it gets standardized?expected -
Where, considering
may be a good place to change APIs?std2