This paper revises [P0323r5], which applied feedback obtained from LEWG and EWG.
r6 takes in account the LWG feedback in Jacksonville.
-
Locate
insideunexpected header and section.< expected > -
Define friend functions for equality operators and
.swap -
Follows as much as possible
kind of wording.variant -
Reword
overload.emplace ( initializer_list ) -
Use
istrait true/false. -
Simplify Effects clause when there is a simple return.
We have added some additional open points that have not been identified previously after the LWG’s feedback:
-
Has
a sense?unexpected < void > -
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.unexpect_t
These should be fixed in another paper if LEWG considered the fix necessary.
[P0323r4] contains motivation, design rationale, implementability information, sample usage, history, alternative designs and related types. r6 and r5 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 {}; }}}
A program that instantiates the definition of for a
non-object type, a function type, an array object, an object type or
an object type cv-qualified is ill-formed.
A program that instantiates the definition of template for a reference type or for possibly cv-qualified types , or for the parameter or
for a reference type or for possibly cv-qualified type for the parameter
is ill-formed.
1.4. �.�.3 Definitions [expected.defs]
An instance of is said to be valued if it contains a object of
type . An instance of is said to be disapointed if it
contains an object of type .
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 constexpr expected ( const expected < U , G >& ); template < class U , class G > EXPLICIT constexpr expected ( expected < U , G >&& ); template < class U = T > EXPLICIT 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 unexpect ; // exposition only }; };
Any instance of at any given time either contains a value of
type or a value of type within their 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 shall be allocated in a region of the storage
suitably aligned for the types and . Members , and are provided for exposition only. indicates whether the object is valued.
shall be or shall be an object type that is not array and shall satisfy the requirements
of (Table 27).
shall be object type that is not array and shall satisfy the requirements of (Table 27).
1.6. �.�.4.1 Constructors [expected.object.ctor]
constexpr expected ();
Effects: Value-Initializes if is not .
Postconditions: .
Throws: Any exception thrown by the selected constructor of .
Remarks: If value-initialization of is a constexpr constructor or is this constructor shall be constexpr. This constructor shall not participate in
overload resolution unless is true or is .
constexpr expected ( const expected & rhs );
Effects: If , initializes as if
direct-non-list-initializing an object of type with the expression if is not .
If , initializes as if
direct-non-list-initializing an object of type with the
expression .
Postconditions: .
Throws: Any exception thrown by the selected constructors of or .
Remarks: This constructor shall be defined as deleted unless is true or is and is true. If is true or is and is true,
this constructor shall be a constexpr constructor.
constexpr expected ( expected && rhs ) noexcept ( see below );
Effects: If , initializes as if
direct-non-list-initializing an object of type with the expression (if is not ).
If , initializes as if
direct-non-list-initializing an object of type with the
expression .
is unchanged.
Postconditions: .
Throws: Any exception thrown by the selected constructor of or .
Remarks: The expression inside is equivalent to: is true or is and is true. This constructor shall not participate in
overload resolution unless is true and is true. If is true or is and is true,
this constructor shall be a constexpr constructor.
template < class U , class G > EXPLICIT constexpr expected ( const expected < U , G >& rhs );
Effects: If , initializes as if
direct-non-list-initializing an object of type with the expression ,
if is not .
If initializes as if
direct-non-list-initializing an object of type with the
expression .
Postconditions: .
Throws: Any exception thrown by the selected constructor of or .
Remarks: This constructor shall not participate in overload resolution unless: and are or
-
isis_constructible_v < T , const U &> true, -
isis_constructible_v < T , expected < U , G >&> false, -
isis_constructible_v < T , expected < U , G >&&> false, -
isis_constructible_v < T , const expected < U , G >&> false, -
isis_constructible_v < T , const expected < U , G >&&> false, -
isis_convertible_v < expected < U , G >& , T > false, -
isis_convertible_v < expected < U , G >&& , T > false, -
isis_convertible_v < const expected < U , G >& , T > falseand -
isis_convertible_v < const expected < U , G >&& , T > false
and
-
isis_constructible_v < E , const G &> true, -
isis_constructible_v < unexpected < E > , expected < U , G >&> false, -
isis_constructible_v < unexpected < E > , expected < U , G >&&> false, -
isis_constructible_v < unexpected < E > , const expected < U , G >&> false, -
isis_constructible_v < unexpected < E > , const expected < U , G >&&> false, -
isis_convertible_v < expected < U , G >& , unexpected < E >> false, -
isis_convertible_v < expected < U , G >&& , unexpected < E >> false, -
isis_convertible_v < const expected < U , G >& , unexpected < E >> falseand -
isis_convertible_v < const expected < U , G >&& , unexpected < E >> false.
The constructor is explicit if and only if
-
andT are notU andvoid isis_convertible_v < U const & , T > falseor -
isis_convertible_v < const G & , E > false.
template < class U , class G > EXPLICIT constexpr expected ( expected < U , G >&& rhs );
Effects: If initializes as if
direct-non-list-initializing an object of type with the expression or nothing if is .
If , initializes as if
direct-non-list-initializing an object of type with the
expression .
is unchanged.
Postconditions: .
Throws: Any exception thrown by operations specified in the effect clause.
Remarks: This constructor shall not participate in overload resolution unless: and are or
-
isis_constructible_v < T , U &&> true, -
isis_constructible_v < T , expected < U , G >&> false, -
isis_constructible_v < T , expected < U , G >&&> false, -
isis_constructible_v < T , const expected < U , G >&> false, -
isis_constructible_v < T , const expected < U , G >&&> false, -
isis_convertible_v < expected < U , G >& , T > false, -
isis_convertible_v < expected < U , G >&& , T > false, -
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, -
isis_constructible_v < unexpected < E > , expected < U , G >&> false, -
isis_constructible_v < unexpected < E > , expected < U , G >&&> false, -
isis_constructible_v < unexpected < E > , const expected < U , G >&> false, -
isis_constructible_v < unexpected < E > , const expected < U , G >&&> false, -
isis_convertible_v < expected < U , G >& , unexpected < E >> false, -
isis_convertible_v < expected < U , G >&& , unexpected < E >> false, -
isis_convertible_v < const expected < U , G >& , unexpected < E >> falseand -
isis_convertible_v < const expected < U , G >&& , unexpected < E >> false.
The constructor is explicit if and only if
-
andT are notU andvoid isis_convertible_v < U && , T > falseor -
isis_convertible_v < G && , E > false.
template < class U = T > EXPLICIT constexpr expected ( U && v );
Effects: Initializes as if direct-non-list-initializing an
object of type with the expression .
Postconditions: .
Throws: Any exception thrown by the selected constructor of .
Remarks: If 's selected constructor is a constexpr constructor, this
constructor shall be a constexpr constructor. This constructor shall not
participate in overload resolution unless
-
is notT ,void -
isis_constructible_v < T , U &&> true, -
isis_same_v < remove_cvref_t < U > , in_place_t > false, -
isis_same_v < expected < T , E > , remove_cvref_t < U >> falseand -
isis_same_v < unexpected < E > , remove_cvref_t < U >> false.
The constructor is explicit if and only if is false.
template < class G = E > EXPLICIT constexpr expected ( const unexpected < G >& e );
Effects: Initializes as if direct-non-list-initializing
an object of type with the expression .
Postconditions: .
Throws: Any exception thrown by the selected constructor of .
Remark: If 's selected constructor is a constexpr constructor,
this constructor shall be a constexpr constructor. This constructor shall not
participate in overload resolution unless is true. The
constructor is explicit if and only if is false.
template < class G = E > EXPLICIT constexpr expected ( unexpected < G >&& e );
Effects: Initializes as if direct-non-list-initializing
an object of type with the expression .
Postconditions: .
Throws: Any exception thrown by the selected constructor of .
Remark: If 's selected constructor is a constexpr constructor,
this constructor shall be a constexpr constructor. The expression inside is equivalent to: is true. This
constructor shall not participate in overload resolution unless is true. The constructor is explicit if and only if is false.
template < class ... Args > constexpr explicit expected ( in_place_t , Args && ... args );
Effects: Initializes as if direct-non-list-initializing an
object of type with the arguments if is not .
Postconditions: .
Throws: Any exception thrown by the selected constructor of .
Remarks: If 's constructor selected for the initialization is a constexpr
constructor, this constructor shall be a constexpr constructor. This
constructor shall not participate in overload resolution unless is and or is not and is true.
template < class U , class ... Args > constexpr explicit expected ( in_place_t , initializer_list < U > il , Args && ... args );
Effects: Initializes as if direct-non-list-initializing an
object of type with the arguments .
Postconditions: .
Throws: Any exception thrown by the selected constructor of .
Remarks: If 's constructor selected for the initialization is a constexpr
constructor, this constructor shall be a constexpr constructor. This
constructor shall not participate in overload resolution unless is not and is true.
template < class ... Args > constexpr explicit expected ( unexpect_t , Args && ... args );
Effects: Initializes as if direct-non-list-initializing
an object of type with the arguments .
Postconditions: .
Throws: Any exception thrown by the selected constructor of .
Remarks: If 's constructor selected for the initialization is a
constexpr constructor, this constructor shall be a constexpr constructor. This
constructor shall not participate in overload resolution unless is true.
template < class U , class ... Args > constexpr explicit expected ( unexpect_t , initializer_list < U > il , Args && ... args );
Effects: Initializes as if direct-non-list-initializing
an object of type with the arguments .
Postconditions: .
Throws: Any exception thrown by the selected constructor of .
Remarks: If 's constructor selected for the initialization is a
constexpr constructor, this constructor shall be a constexpr constructor. This
constructor shall not participate in overload resolution unless is true.
1.7. �.�.4.2 Destructor [expected.object.dtor]
~ expected ();
Effects: If is not and is false and , calls . If is false and , calls .
Remarks: If is or is true and is true then this destructor shall be a
trivial destructor.
1.8. �.�.4.3 Assignment [expected.object.assign]
expected & operator = ( const expected & rhs ) noexcept ( see below );
Effects:
If and ,
-
assigns
to* rhs ifval is notT ;void
otherwise if and ,
-
assigns
tounexpected ( rhs . error ()) ;unexpect
otherwise if and ,
-
if
isT void -
initializes
as if direct-non-list-initializing an object of typeunexpect withunexpected < E > . Eitherunexpected ( rhs . error ()) -
the constructor didn’t throw, set
tohas_val false, or -
the constructor did throw, and nothing was changed.
-
-
otherwise if
isis_nothrow_copy_constructible_v < E > true-
destroys
by callingval ,val . ~ T () -
initializes
as if direct-non-list-initializing an object of typeunexpect withunexpected < E > and setunexpected ( rhs . error ()) tohas_val false.
-
-
otherwise if
isis_nothrow_move_constructible_v < E > true-
constructs a
fromunexpected < E > tmp (this can throw),unexpected ( rhs . error ()) -
destroys
by callingval ,val . ~ T () -
initializes
as if direct-non-list-initializing an object of typeunexpect withunexpected < E > and setstd :: move ( tmp ) tohas_val false.
-
otherwise
-
constructs a
fromT tmp (this can throw),* this -
destroys
by callingval ,val . ~ T () -
initializes
as if direct-non-list-initializing an object of typeunexpect withunexpected < E > . Either,unexpected ( rhs . error ()) -
the last constructor didn’t throw, set
tohas_val false, or -
the last constructor did throw, so move-construct the
fromT back into the expected storage (which can’t throw astmp isis_nothrow_move_constructible_v < T > true), and rethrow the exception.
-
otherwise
-
if
isT destroysvoid by callingunexpect and setunexpect . ~ unexpected < E > () tohas_val true, -
otherwise if
isis_nothrow_copy_constructible_v < T > true-
destroys
by callingunexpect unexpect . ~ unexpected < E > () -
initializes
as if direct-non-list-initializing an object of typeval withT ;* rhs
-
-
otherwise if
isis_nothrow_move_constructible_v < T > true-
constructs a
fromT tmp (this can throw),* rhs -
destroys
by callingunexpect unexpect . ~ unexpected < E > () -
initializes
as if direct-non-list-initializing an object of typeval withT ;std :: move ( tmp )
-
-
otherwise
-
constructs a
fromunexpected < E > tmp (which can throw),unexpected ( this -> error ()) -
destroys
by callingunexpect ,unexpect . ~ unexpected < E > () -
initializes
as if direct-non-list-initializing an object of typeval withT . Either,* rhs -
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 isis_nothrow_move_constructible_v < E > true), and rethrow the exception.
-
Returns: .
Postconditions: .
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 shall be defined as deleted unless
-
isT andvoid isis_copy_assignable_v < E > trueandisis_copy_constructible_v < E > trueor -
is notT andvoid isis_copy_assignable_v < T > trueandisis_copy_constructible_v < T > trueandisis_copy_assignable_v < E > trueandisis_copy_constructible_v < E > trueand (isis_nothrow_move_constructible_v < E > trueorisis_nothrow_move_constructible_v < T > true).
expected & operator = ( expected && rhs ) noexcept ( see below );
Effects:
If and ,
-
move assign
to* rhs ifval is notT ;void
otherwise if and ,
-
move assign
tounexpected ( rhs . error ()) ;unexpect
otherwise if and ,
-
if
isT void -
initializes
as if direct-non-list-initializing an object of typeunexpect withunexpected < E > . Eitherunexpected ( move ( rhs ). error ()) -
the constructor didn’t throw, set
tohas_val false, or -
the constructor did throw, and nothing was changed.
-
-
otherwise if
isis_nothrow_move_constructible_v < E > true-
destroys
by callingval ,val . ~ T () -
initializes
as if direct-non-list-initializing an object of typeunexpect withunexpected < E > ;unexpected ( std :: move ( rhs . error ()))
-
-
otherwise
-
move constructs a
fromT tmp (which can’t throw as* this is nothrow-move-constructible),T -
destroys
by callingval ,val . ~ T () -
initializes
as if direct-non-list-initializing an object of typeunexpect withunexpected_type < E > . Either,unexpected ( std :: move ( rhs . error ())) -
The constructor didn’t throw, so mark the expected as holding a
, orunexpected_type < E > -
The constructor did throw, so move-construct the
fromT back into the expected storage (which can’t throw astmp is nothrow-move-constructible), and rethrow the exception.T
-
otherwise and ,
-
if
isT destroysvoid by callingunexpect unexpect . ~ unexpected < E > () -
otherwise if
isis_nothrow_move_constructible_v < T > true-
destroys
by callingunexpect ,unexpect . ~ unexpected < E > () -
initializes
as if direct-non-list-initializing an object of typeval withT ;* std :: move ( rhs )
-
-
otherwise
-
move constructs a
fromunpepected_type < E > tmp (which can’t throw asunexpected ( this -> error ()) is nothrow-move-constructible),E -
destroys
by callingunexpect ,unexpect . ~ unexpected < E > () -
initializes
as if direct-non-list-initializing an object of typeval withT . Either,* std :: move ( rhs ) -
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 rethrow the exception.E
-
Returns: .
Postconditions: .
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 shall be defined as deleted unless
-
isT andvoid isis_nothrow_move_constructible_v < E > trueandisis_nothrow_move_assignable_v < E > true.
or
-
is notT andvoid isis_move_constructible_v < T > trueandisis_move_assignable_v < T > trueandisis_nothrow_move_constructible_v < E > trueandisis_nothrow_move_assignable_v < E > true.
template < class U = T > expected < T , E >& operator = ( U && v );
Effects:
If , assigns to ;
otherwise if is true
-
destroys
by callingunexpect ,unexpect . ~ unexpected < E > () -
initializes
as if direct-non-list-initializing an object of typeval withT andstd :: forward < U > ( v ) -
set
tohas_val true;
otherwise
-
move constructs a
fromunexpected < E > tmp (which can’t throw asunexpected ( this -> error ()) is nothrow-move-constructible),E -
destroys
by callingunexpect ,unexpect . ~ unexpected < E > () -
initializes
as if direct-non-list-initializing an object of typeval withT . Either,std :: forward < U > ( v ) -
the constructor didn’t throw, set
tohas_val true, that is settohas_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
-
Returns: .
Postconditions: .
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.
This function shall not participate in overload resolution unless:
-
isis_void_v < T > falseand -
isis_same_v < expected < T , E > , remove_cvref_t < U >> falseand -
isconjunction_v < is_scalar < T > , is_same < T , decay_t < U >>> false, -
isis_constructible_v < T , U > true, -
isis_assignable_v < T & , U > trueand -
isis_nothrow_move_constructible_v < E > true.
template < class G = E > expected < T , E >& operator = ( const unexpected < G >& e );
Effects:
If , assigns to ;
otherwise
-
destroys
by callingval ifval . ~ T () is notT ,void -
initializes
as if direct-non-list-initializing an object of typeunexpect withunexpected < E > and setunexpected ( e . error ()) tohas_val false.
Returns: .
Postconditions: .
Remarks: If any exception is thrown, remains unchanged.
This signature shall not participate in overload resolution unless is true and is true.
expected < T , E >& operator = ( unexpected < G > && e );
Effects:
If , move assign to ;
otherwise
-
destroys
by callingval ifval . ~ T () is notT ,void -
initializes
as if direct-non-list-initializing an object of typeunexpect withunexpected < E > and setunexpected ( std :: move ( e . error ())) tohas_val false.
Returns: .
Postconditions: .
Remarks: If any exception is thrown, remains unchanged.
This signature shall not participate in overload resolution unless is true and is true.
void expected < void , E >:: emplace ();
Effects:
If
-
destroys
by callingunexpect ,unexpect . ~ unexpected < E > () -
set
tohas_val true
Postconditions: .
Throws: Nothing
template < class ... Args > T & emplace ( Args && ... args );
Effects:
If , assigns as if
constructing an object of type with the arguments
otherwise if is true
-
destroys
by callingunexpect ,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 callingunexpect ,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 a
fromunexpected < E > tmp ,unexpected ( this -> error ()) -
destroys
by callingunexpect ,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
-
Postconditions: .
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.
This signature shall not participate in overload resolution unless: is not and is true.
template < class U , class ... Args > T & emplace ( initializer_list < U > il , Args && ... args );
Effects:
If , assigns as if
constructing an object of type with the arguments
otherwise if is true
-
destroys
by callingunexpect ,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 callingunexpect ,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 a
fromunexpected < E > tmp ,unexpected ( this -> error ()) -
destroys
by callingunexpect ,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
-
Postconditions: .
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.
The function shall not participate in overload resolution unless: is not and is true.
1.9. �.�.4.4 Swap [expected.object.swap]
void swap ( expected < T , E >& rhs ) noexcept ( see below );
Effects: if and ,
-
if
is notT callsvoid ,using std :: swap ; swap ( val , rhs . val )
otherwise if and ,
-
calls
,using std :: swap ; swap ( unexpect , rhs . unexpect )
otherwise if and ,
-
calls
,rhs . swap ( * this )
otherwise
-
if
isT void -
initializes
as if direct-non-list-initializing an object of typeunexpect withunexpected < E > . Eitherunexpected ( std :: move ( rhs )) -
the constructor didn’t throw, set
tohas_val false, destroysby callingrhs . unexpect setrhs . unexpect . ~ unexpected < E > () torhs . has_val true. -
the constructor did throw, rethrow the exception.
-
-
otherwise if
isis_nothrow_move_constructible_v < E > true,-
the
ofunexpect is moved to a temporary variablerhs of typetmp ,unexpected_type -
followed by destruction of
as if byunexpect ,rhs . unexpect . ~ unexpected < E > () -
is direct-initialized fromrhs . val . Eitherstd :: move ( * this ) -
the constructor didn’t throw
-
destroy
as if byval ,this -> val . ~ T () -
the
ofunexpect is direct-initialized fromthis , after this,std :: move ( tmp ) does not contain a value; andthis .bool ( rhs )
-
-
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
-
-
-
otherwise if
isis_nothrow_move_constructible_v < T > true,-
is moved to a temporary variableval of typetmp ,T -
followed by destruction of
as if byval ,val . ~ T () -
the
is direct-initialized fromunexpect . Eitherunexpected ( std :: move ( other . error ())) -
the constructor didn’t throw
-
destroy
as if byrhs . unexpect ,rhs . unexpect . ~ unexpected < E > () -
is direct-initialized fromrhs . val , after this,std :: move ( tmp ) does not contain a value; andthis .bool ( rhs )
-
-
the constructor did throw, so move-construct the
fromT back into the expected storage (which can’t throw astmp is nothrow-move-constructible), and re-throw the exception.T
-
-
-
otherwise, the function is not defined.
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.
The function shall not
participate in overload resolution unless:
-
Lvalues of type
shall beT ,Swappable -
Lvalues of type
shall beE ,Swappable -
isis_void_v < T > trueor -
isis_void_v < T > falseand-
isis_move_constructible_v < T > true, -
isis_move_constructible_v < E > trueand -
isis_move_constructible_v < E > trueorisis_move_constructible_v < T > true.
-
1.10. �.�.4.5 Observers [expected.object.observe]
constexpr const T * operator -> () const ; T * operator -> ();
Requires: .
Returns: .
Remarks: Unless is a user-defined type with overloaded unary ,
the first operator shall be a constexpr function.
The operator shall not participate in overload resolution unless: is not .
constexpr const T & operator * () const & ; T & operator * () & ;
Requires: .
Returns: .
Remarks: The first operator shall be a constexpr function.
The operator shall not participate in overload resolution unless: is not .
constexpr T && operator * () && ; constexpr const T && operator * () const && ;
Requires: .
Returns: .
Remarks: This operator shall be a constexpr function.
The operator shall not participate in overload resolution unless: is not .
constexpr explicit operator bool () noexcept ;
Returns: .
Remarks: This operator shall be a constexpr function.
constexpr bool has_value () const noexcept ;
Returns: .
Remarks: This function shall be a constexpr function.
constexpr void expected < void , E >:: value () const ;
Throws: if .
constexpr const T & expected :: value () const & ; constexpr T & expected :: value () & ;
Returns: , if .
Throws: if .
Remarks: These functions shall be constexpr functions.
The operator shall not participate in overload resolution unless: is not .
constexpr T && expected :: value () && ; constexpr const T && expected :: value () const && ;
Returns: , if .
Throws: if .
Remarks: These functions shall be constexpr functions.
The operator shall not participate in overload resolution unless: is not .
constexpr const E & error () const & ; constexpr E & error () & ;
Requires: .
Returns: .
Remarks: The first function shall be a constexpr function.
constexpr E && error () && ; constexpr const E && error () const && ;
Requires: .
Returns: .
Remarks: The first function shall be a constexpr function.
template < class U > constexpr T value_or ( U && v ) const & ;
Returns: .
Remarks: If 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 );
Requires: The expressions and shall be well-formed and its result
shall be convertible to .
Returns: If , false; otherwise if , ; otherwise true if and are or otherwise.
Remarks: Specializations of this function template, for which and are or and are core constant expression, shall be functions.
template < class T1 , class E1 , class T2 , class E2 > constexpr bool operator != ( const expected < T1 , E1 >& x , const expected < T2 , E2 >& y );
Requires: The expressions and shall be
well-formed and its result shall be convertible to .
Returns: If , true; otherwise if , ; otherwise true if and are or .
Remarks: Specializations of this function template, for which and are or and are core
constant expression, shall be 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 );
Requires: and are not and the expression shall be well-formed
and its result shall be 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 );
Requires: and are not and the expression shall be well-formed
and its result shall be 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 );
Requires: The expression shall be well-formed and
its result shall be 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 );
Requires: The expression shall be well-formed and
its result shall be 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 )));
Effects: Calls .
Remarks: This function shall not participate in overload resolution unless is or is true, is true and is true and is true.
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 unexpected ( const unexpected < Err >& ); template < class Err > constexpr EXPLICIT 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 bellow ); 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 > ;
�.�.5.2.1 Constructors [expected.unexpected.ctor] {#expected.unexpected.ctor}
template < class Err > constexpr explicit unexpected ( Err && e );
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 E’s selected constructor is a constexpr constructor, this constructor shall be a constexpr constructor.
This constructor shall not participate in overload resolution unless is true, is false and is false.
template < class ... Args > constexpr explicit unexpected ( in_place_t , Args && ...);
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 E’s selected constructor is a constexpr constructor, this constructor shall be a constexpr constructor.
This constructor shall not participate in overload resolution unless is true.
template < class U , class ... Args > constexpr explicit unexpected ( in_place_t , initializer_list < U > , Args && ...);
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 E’s selected constructor is a constexpr constructor, this constructor shall be a constexpr constructor.
This constructor shall not participate in overload resolution unless is true.
template < class Err > constexpr EXPLICIT unexpected ( const unexpected < Err >& e );
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: This constructor participates in overload resolution unless
-
isstd :: is_constructible_v < E , Err > true, -
isis_constructible_v < E , unexpected < Err >&> false, -
isis_constructible_v < E , unexpected < Err >&&> false, -
isis_constructible_v < E , const unexpected < Err >&> false, -
isis_constructible_v < E , const unexpected < Err >&&> false, -
isis_convertible_v < unexpected < Err >& , E > false, -
isis_convertible_v < unexpected < Err >&& , E > false, -
isis_convertible_v < const unexpected < Err >& , E > false, and -
isis_convertible_v < const unexpected < Err >&& , E > false.
This constructor is if and only if is false.
template < class Err > constexpr EXPLICIT unexpected ( unexpected < Err > && e );
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: This constructor participates in overload resolution unless
-
isstd :: is_constructible_v < E , Err &&> true, -
isis_constructible_v < E , unexpected < Err >&> false, -
isis_constructible_v < E , unexpected < Err >&&> false, -
isis_constructible_v < E , const unexpected < Err >&> false, -
isis_constructible_v < E , const unexpected < Err >&&> false, -
isis_convertible_v < unexpected < Err >& , E > false, -
isis_convertible_v < unexpected < Err >&& , E > false, -
isis_convertible_v < const unexpected < Err >& , E > false, and -
isis_convertible_v < const unexpected < Err >&& , E > false.
This constructor is if and only if is false
�.�.5.2.2 Assignment [expected.unexpected.assign] {#expected.unexpected.assign}
�.�.5.2.3 Observers [expected.unexpected.observe] {#expected.unexpected.observe}
constexpr const E & value () const & ; constexpr E & value () & ;
Returns: .
constexpr E && value () && ; constexpr E const && value () const && ;
Returns: .
�.�.5.2.4 Swap [expected.unexpected.ctor] {#expected.unexpected.swap}
void swap ( unexpected & other ) noexcept ( see bellow );
Requires: is true
Effects: Equivalent to .
Throws:
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: .
�.�.5.2.5 Specialized algorithms [expected.unexpected.specalg] {#expected.unexpected.specalg}
template < class E1 > friend void swap ( unexpected < E1 >& x , unexpected < E1 >& y ) noexcept ( noexcept ( x . swap ( y )));
Effects: Equivalent to .
Remarks: This function shall not participate in overload resolution unless is true.
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 an 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: Initialize with .
Postconditions: 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 { explicit unexpect_t () = default ; }; inline constexpr unexpect_t unexpect {};
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?unexpected
-
-
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
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