Class template 
Class template 
- 
     A value of type T 
- 
     A value of type E 
The interface can be queried as to whether the underlying value is the expected value (of type 
The interface and the rational are based on 
1. Revision History
This paper updates [P0323r10] following a few LWG reviews on 2021-09-10 and later. Remove conversions from 
In the 2021-04-06 LEWG telecon, LEWG decided to target the IS instead of a TS.
Poll: [P0323r9] should add a feature test macro, change the
namespace/header from 
| SF | F | N | A | SA | 
| 6 | 6 | 5 | 1 | 0 | 
Various iterations of this paper have been reviewed by WG21 over time:
Related papers:
- 
     [P0323r3] was the last paper with a rationale. LEWG asked that the rationale be dropped from r4 onwards. The rationale is now back in r10, to follow the new LEWG policy of preserving rationales. 
- 
     Some design issues were discussed by LWG and answered in [P1051r0]. 
- 
     [P0650r2] proposes monadic interfaces for expected 
- 
     [P0343r1] proposes meta-programming high-order functions. 
- 
     [P0262r1] is a related proposal for status / optional value. 
- 
     [P0157R0] describes when to use each of the different error report mechanism. 
- 
     [P0762r0] discussed how this paper interacts with boost.Outcome. 
- 
     [P1095r0] proposes zero overhead deterministic failure. 
- 
     [P0709r4] discussed zero-overhead deterministic exceptions. 
- 
     [P1028R3] proposes status_code error 
2. Motivation
C++'s two main error mechanisms are exceptions and return codes. Characteristics of a good error mechanism are:
- 
     Error visibility: Failure cases should appear throughout the code review: debugging can be painful if errors are hidden. 
- 
     Information on errors: Errors should carry information from their origin, causes and possibly the ways to resolve it. 
- 
     Clean code: Treatment of errors should be in a separate layer of code and as invisible as possible. The reader could notice the presence of exceptional cases without needing to stop reading. 
- 
     Non-Intrusive error: Errors should not monopolize the communication channel dedicated to normal code flow. They must be as discrete as possible. For instance, the return of a function is a channel that should not be exclusively reserved for errors. 
The first and the third characteristic seem contradictory and deserve further explanation. The former points out that errors not handled should appear clearly in the code. The latter tells us that error handling must not interfere with the legibility, meaning that it clearly shows the normal execution flow.
Here is a comparison between the exception and return codes:
| Exception | Return error code | |
| Visibility | Not visible without further analysis of the code. However, if an exception is thrown, we can follow the stack trace. | Visible at the first sight by watching the prototype of the called function. However ignoring return code can lead to undefined results and it can be hard to figure out the problem. | 
| Informations | Exceptions can be arbitrarily rich. | Historically a simple integer. Nowadays, the header <system_error> provides richer error code. | 
| Clean code | Provides clean code, exceptions can be completely invisible for the caller. | Force you to add, at least, a if statement after each function call. | 
| Non-Intrusive | Proper communication channel. | Monopolization of the return channel. | 
We can do the same analysis for the 
- 
     Error visibility: It takes the best of the exception and error code. It’s visible because the return type is expected < T , E > 
- 
     Information: Arbitrarily rich. 
- 
     Clean code: The monadic interface of expected provides a framework delegating the error handling to another layer of code. Note that expected < T , E > 
- 
     Non-Intrusive: Use the return channel without monopolizing it. 
Other notable characteristics of 
- 
     Associates errors with computational goals. 
- 
     Naturally allows multiple errors inflight. 
- 
     Teleportation possible. 
- 
     Across thread boundaries. 
- 
     On weak executors which don’t support thread-local storage. 
- 
     Across no-throw subsystem boundaries. 
- 
     Across time: save now, throw later. 
- 
     Collect, group, combine errors. 
- 
     Much simpler for a compiler to optimize. 
2.1. Sample Usecase
The following is how WebKit-based browsers parse URLs and use 
template < typename CharacterType > Expected < uint32_t , URLParser :: IPv4PieceParsingError > URLParser :: parseIPv4Piece ( CodePointIterator < CharacterType >& iterator , bool & didSeeSyntaxViolation ) { enum class State : uint8_t { UnknownBase , Decimal , OctalOrHex , Octal , Hex , }; State state = State :: UnknownBase ; Checked < uint32_t , RecordOverflow > value = 0 ; if ( ! iterator . atEnd () && * iterator == '.' ) return makeUnexpected ( IPv4PieceParsingError :: Failure ); while ( ! iterator . atEnd ()) { if ( isTabOrNewline ( * iterator )) { didSeeSyntaxViolation = true; ++ iterator ; continue ; } if ( * iterator == '.' ) { ASSERT ( ! value . hasOverflowed ()); return value . unsafeGet (); } switch ( state ) { case State :: UnknownBase : if ( UNLIKELY ( * iterator == '0' )) { ++ iterator ; state = State :: OctalOrHex ; break ; } state = State :: Decimal ; break ; case State :: OctalOrHex : didSeeSyntaxViolation = true; if ( * iterator == 'x' || * iterator == 'X' ) { ++ iterator ; state = State :: Hex ; break ; } state = State :: Octal ; break ; case State :: Decimal : if ( ! isASCIIDigit ( * iterator )) return makeUnexpected ( IPv4PieceParsingError :: Failure ); value *= 10 ; value += * iterator - '0' ; if ( UNLIKELY ( value . hasOverflowed ())) return makeUnexpected ( IPv4PieceParsingError :: Overflow ); ++ iterator ; break ; case State :: Octal : ASSERT ( didSeeSyntaxViolation ); if ( * iterator < '0' || * iterator > '7' ) return makeUnexpected ( IPv4PieceParsingError :: Failure ); value *= 8 ; value += * iterator - '0' ; if ( UNLIKELY ( value . hasOverflowed ())) return makeUnexpected ( IPv4PieceParsingError :: Overflow ); ++ iterator ; break ; case State :: Hex : ASSERT ( didSeeSyntaxViolation ); if ( ! isASCIIHexDigit ( * iterator )) return makeUnexpected ( IPv4PieceParsingError :: Failure ); value *= 16 ; value += toASCIIHexValue ( * iterator ); if ( UNLIKELY ( value . hasOverflowed ())) return makeUnexpected ( IPv4PieceParsingError :: Overflow ); ++ iterator ; break ; } } ASSERT ( ! value . hasOverflowed ()); return value . unsafeGet (); } 
These results are then accumulated in a vector , and different failure conditions are handled differently. An important fact to internalize is that the first failure encountered isn’t necessarily the one which is returned, which is why exceptions aren’t a good solution here: parsing must continue.
template < typename CharacterTypeForSyntaxViolation , typename CharacterType > Expected < URLParser :: IPv4Address , URLParser :: IPv4ParsingError > URLParser :: parseIPv4Host ( const CodePointIterator < CharacterTypeForSyntaxViolation >& iteratorForSyntaxViolationPosition , CodePointIterator < CharacterType > iterator ) { Vector < Expected < uint32_t , URLParser :: IPv4PieceParsingError > , 4 > items ; bool didSeeSyntaxViolation = false; if ( ! iterator . atEnd () && * iterator == '.' ) return makeUnexpected ( IPv4ParsingError :: NotIPv4 ); while ( ! iterator . atEnd ()) { if ( isTabOrNewline ( * iterator )) { didSeeSyntaxViolation = true; ++ iterator ; continue ; } if ( items . size () >= 4 ) return makeUnexpected ( IPv4ParsingError :: NotIPv4 ); items . append ( parseIPv4Piece ( iterator , didSeeSyntaxViolation )); if ( ! iterator . atEnd () && * iterator == '.' ) { ++ iterator ; if ( iterator . atEnd ()) syntaxViolation ( iteratorForSyntaxViolationPosition ); else if ( * iterator == '.' ) return makeUnexpected ( IPv4ParsingError :: NotIPv4 ); } } if ( ! iterator . atEnd () || ! items . size () || items . size () > 4 ) return makeUnexpected ( IPv4ParsingError :: NotIPv4 ); for ( const auto & item : items ) { if ( ! item . hasValue () && item . error () == IPv4PieceParsingError :: Failure ) return makeUnexpected ( IPv4ParsingError :: NotIPv4 ); } for ( const auto & item : items ) { if ( ! item . hasValue () && item . error () == IPv4PieceParsingError :: Overflow ) return makeUnexpected ( IPv4ParsingError :: Failure ); } if ( items . size () > 1 ) { for ( size_t i = 0 ; i < items . size () - 1 ; i ++ ) { if ( items [ i ]. value () > 255 ) return makeUnexpected ( IPv4ParsingError :: Failure ); } } if ( items [ items . size () - 1 ]. value () >= pow256 ( 5 - items . size ())) return makeUnexpected ( IPv4ParsingError :: Failure ); if ( didSeeSyntaxViolation ) syntaxViolation ( iteratorForSyntaxViolationPosition ); for ( const auto & item : items ) { if ( item . value () > 255 ) syntaxViolation ( iteratorForSyntaxViolationPosition ); } if ( UNLIKELY ( items . size () != 4 )) syntaxViolation ( iteratorForSyntaxViolationPosition ); IPv4Address ipv4 = items . takeLast (). value (); for ( size_t counter = 0 ; counter < items . size (); ++ counter ) ipv4 += items [ counter ]. value () * pow256 ( 3 - counter ); return ipv4 ; } 
2.2. Error retrieval and correction
The major advantage of 
- 
     Ignore it. 
- 
     Delegate the responsibility of error handling to higher layer. 
- 
     Try to resolve the error. 
Because the first behavior might lead to buggy application, we 
ignore the usecase. The handling is dependent of the underlying error 
type, we consider the 
2.3. Impact on the standard
These changes are entirely based on library extensions and do not require any language features beyond what is available in C++20.
3. Design rationale
The same rationale described in [N3672] for 
3.1. Conceptual model of expected < T ,  E > 
   
The interface in this model requires operations such as comparison to 
Additionally, within the affordable limits, we propose the view that 
expected < int , string > ei = 0 ; expected < int , string > ej = 1 ; expected < int , string > ek = unexpected ( string ()); ei = 1 ; ej = unexpected ( E ());; ek = 0 ; ei = unexpected ( E ());; ej = 0 ; ek = 1 ; 
3.2. Default E 
   At the Toronto meeting LEWG decided against having a default 
3.3. Initialization of expected < T ,  E > 
   In cases where 
As in [N3672], the model retained is to initialize either by providing an
already constructed 
string s { "STR" }; expected < string , errc > es { s }; // requires Copyable<T> expected < string , errc > et = s ; // requires Copyable<T> expected < string , errc > ev = string { "STR" }; // requires Movable<T> expected < string , errc > ew ; // expected value expected < string , errc > ex {}; // expected value expected < string , errc > ey = {}; // expected value expected < string , errc > ez = expected < string , errc > {}; // expected value 
In order to create an unexpected object, the deduction guide 
expected < string , int > ep { unexpected ( -1 )}; // unexpected value, requires Movable<E> expected < string , int > eq = unexpected ( -1 ); // unexpected value, requires Movable<E> 
As in [N3672], and in order to avoid calling move/copy constructor of 
expected < MoveOnly , errc > eg ; // expected value expected < MoveOnly , errc > eh {}; // expected value expected < MoveOnly , errc > ei { in_place }; // calls MoveOnly{} in place expected < MoveOnly , errc > ej { in_place , "arg" }; // calls MoveOnly{"arg"} in place 
To avoid calling move/copy constructor of 
expected < int , string > ei { unexpect }; // unexpected value, calls string{} in place expected < int , string > ej { unexpect , "arg" }; // unexpected value, calls string{"arg"} in place 
An alternative name for 
The alternative and also comprehensive initialization approach, which is
compatible with the default construction of 
3.4. Never-empty guarantee
As for 
This implies that 
In order to ensure this property the types 
Note however that these constraints are applied only to the operations that need them.
If false, the 
3.5. The default constructor
Similar data structure includes 
- 
     std :: optional < T > 
- 
     std :: variant < T1 ,..., Tn > T1 
- 
     std :: future < T > 
- 
     std :: optional < T > boost :: variant < nullopt_t , T > 
This raises several questions about 
- 
     Should the default constructor of expected < T , E > variant < T , unexpected < E >> variant < unexpected < E > , T > 
- 
     Should the default constructor of expected < T , nullopt_t > optional < T > expected < T , E > unexpected ( E ()) variant < unexpected < E > , T > 
- 
     Should expected < T , E > array < expected < T , E >> 
Requiring 
The authors consider the arguments in [N3527] valid for 
3.6. Could Error void 
   
3.7. Conversion from T 
   An object of type 
expected < int , errc > ei = 1 ; // works 
This convenience feature is not strictly necessary because you can achieve the same effect by using tagged forwarding constructor:
expected < int , errc > ei { in_place , 1 }; 
It has been demonstrated that this implicit conversion is dangerous [a-gotcha-with-optional].
An alternative will be to make it explicit and add a 
expected < int , errc > ei = success ( 1 ); expected < int , errc > ej = unexpected ( ec ); 
The authors consider that it is safer to have the explicit conversion, the
implicit conversion is so friendly that we don’t propose yet an explicit
conversion. In addition 
Further, having 
3.8. Conversion from E 
   An object of type 
expected < string , errc > exp1 = unexpected ( 1 ); expected < string , errc > exp2 = { unexpect , 1 }; exp1 = unexpected ( 1 ); exp2 = { unexpect , 1 }; 
or simply using deduced template parameter for constructors
expected < string , errc > exp1 = unexpected ( 1 ); exp1 = unexpected ( 1 ); 
While some situations would work with the 
expected < vector < int > , errc > get1 () {} return { unexpect , 1 }; } expected < vector < int > , errc > get2 () { return unexpected ( 1 ); } expected < vector < int > , errc > get3 () { return expected < vector < int > , int > { unexpect , 1 }; } expected < vector < int > , errc > get2 () { return unexpected ( 1 ); } 
The usage of 
3.9. Should we support the exp2  =  {} 
   Note also that the definition of 
Now that 
3.10. Observers
In order to be as efficient as possible, this proposal includes observers with
narrow and wide contracts. Thus, the 
3.11. Explicit conversion to bool 
   The rational described in [N3672] for 
if ( expected < char , errc > ch = readNextChar ()) { // ... } 
3.12. has_value () 
   
3.13. Accessing the contained value
Even if 
The rational described in [N3672] for 
3.14. Dereference operator
The indirection operator was chosen because, along with explicit conversion to 
if ( p ) use ( * p ); 
This pattern is used for all sort of pointers (smart or raw) and 
We do not think that providing an implicit conversion to 
Using the indirection operator for an object that does not contain a value is undefined behavior. This behavior offers maximum runtime performance.
3.15. Function value
In addition to the indirection operator, we propose the member function 
void interact () { string s ; cout << "enter number: " ; cin >> s ; expected < int , error > ei = str2int ( s ); try { process_int ( ei . value ()); } catch ( bad_expected_access < error > ) { cout << "this was not a number." ; } } 
The exception thrown is 
3.16. Should expected < T ,  E >:: value () E bad_expected_access < E > 
   As any type can be thrown as an exception, should 
Some argument that standard function should throw exceptions that inherit from 
This could be convenient as the user will have directly the 
If yes, should 
We don’t propose this.
Other have suggested to throw 
An alternative would be to add some customization point that state which exception is thrown but we don’t propose it in this proposal. See the Appendix I.
3.17. Accessing the contained error
Usually, accessing the contained error is done once we know the expected object
has no value. This is why the 
expected < int , errc > getIntOrZero ( istream_range & r ) { auto r = getInt (); // won’t throw if ( ! r && r . error () == errc :: empty_stream ) { return 0 ; } return r ; } 
This behavior could not be obtained with the 
We could as well provide an error access function with a wide contract. We just need to see how to name each one.
3.18. Conversion to the unexpected value
The 
expected < pair < int , int > , errc > getIntRange ( istream_range & r ) { auto f = getInt ( r ); if ( ! f ) return unexpected ( f . error ()); auto m = matchedString ( ".." , r ); if ( ! m ) return unexpected ( m . error ()); auto l = getInt ( r ); if ( ! l ) return unexpected ( l . error ()); return std :: make_pair ( * f , * l ); } 
3.19. Function value_or 
   The function member 
This function is a convenience function that should be a non-member function for 
3.20. Equality operators
As for 
3.21. Comparison operators
Comparison operators between 
3.22. Modifiers
3.23. Resetting the value
Reseting the value of 
3.24. Tag in_place 
   This proposal makes use of the "in-place" tag as defined in [C++17]. This
proposal provides the same kind of "in-place" constructor that forwards
(perfectly) the arguments provided to 
In order to trigger this constructor one has to use the tag 
expected < Big , error > eb { in_place , "1" }; // calls Big{"1"} in place (no moving) expected < Big , error > ec { in_place }; // calls Big{} in place (no moving) expected < Big , error > ed {}; // calls Big{} (expected state) 
3.25. Tag unexpect 
   This proposal provides an "unexpect" constructor that forwards (perfectly) the
arguments provided to 
We need the extra tag to disambiguate certain situations, notably if 
expected < Big , error > eb { unexpect , "1" }; // calls error{"1"} in place (no moving) expected < Big , error > ec { unexpect }; // calls error{} in place (no moving) 
In order to make the tag uniform an additional "expect" constructor could be provided but this proposal doesn’t propose it.
3.26. Requirements on T E 
   Class template 
However in order to ensure the never empty guaranties, 
3.27. Expected references
This proposal doesn’t include 
We need a future proposal.
3.28. Expected void 
   While it could seem weird to instantiate 
3.29. Making expected a literal type
In [N3672], they propose to make 
3.30. Moved from state
We follow the approach taken in 
3.31. I/O operations
For the same reasons as 
3.32. What happens when E 
   When 
3.33. Do we need an expected < T ,  E >:: error_or 
   See [P0786R0].
Do we need to add such an 
This function should work for all the ValueOrError types and so could belong to a future ValueOrError proposal.
Not in this proposal.
3.34. Do we need a expected < T ,  E >:: check_error 
   See [P0786R0].
Do we want to add such a 
This function should work for all the ValueOrError types and so could belong to a future ValueOrError proposal.
Not in this proposal.
3.35. Do we need a expected < T , G >:: adapt_error ( function < E ( G )) 
   We have the constructor 
However sometimes we cannot change either of the error types and we could need to do this transformation. This function help to achieve this goal. The parameter is the function doing the error transformation.
This function can be defined on top of the existing interface.
template < class T , class E > expected < T , G > adapt_error ( expected < T , E > const & e , function < G ( E ) > adaptor ) { if ( ! e ) return adaptor ( e . error ()); else return expected < T , G > ( * e ); } 
Do we want to add such a 
This function should work for all the ValueOrError types and so could belong to a future ValueOrError proposal.
Not in this proposal.
3.36. Zombie name
Re-using the 
4. Wording
4.1. Feature test macro
Add the following line to 17.3.2 [version.syn]:
#define __cpp_lib_execution 201903L // also in <execution> #define __cpp_lib_expected 20yymmL // also in <expected> #define __cpp_lib_filesystem 201703L // also in <filesystem> 
Below, substitute the � character with a number or name the editor finds
appropriate for the sub-section.
4.2. �.� Expected objects [expected]
4.3. �.�.1 In general [expected.general]
This subclause describes class template 
5. �.�.2 Header < expected > 
namespace std { // �.�.3, class template unexpected template < class E > class unexpected ; // �.�.4, class bad_expected_access template < class E > class bad_expected_access ; // �.�.5, Specialization for void template <> class bad_expected_access < void > ; // in-place construction of unexpected values struct unexpect_t { explicit unexpect_t () = default ; }; inline constexpr unexpect_t unexpect {}; // �.�.7, class template expected template < class T , class E > class expected ; // �.�.8, class template expected<cv void, E> template < class T , class E > requires is_void_v < T > class expected < T , E > ; } 
5.1. �.�.3 Unexpected objects [expected.unexpected]
5.2. �.�.3.1 General [expected.un.general]
This subclause describes class template 
5.3. �.�.3.2 Class template unexpected 
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 && ); constexpr unexpected & operator = ( const unexpected & ) = default ; constexpr unexpected & operator = ( unexpected && ) = default ; constexpr const E & value () const & noexcept ; constexpr E & value () & noexcept ; constexpr const E && value () const && noexcept ; constexpr E && value () && noexcept ; constexpr void swap ( unexpected & other ) noexcept ( see below ); template < class E2 > friend constexpr bool operator == ( const unexpected & , const unexpected < E2 >& ); friend constexpr void swap ( unexpected & x , unexpected & 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 
5.3.1. �.�.3.2.1 Constructors [expected.un.ctor]
template < class Err = E > constexpr explicit unexpected ( Err && e ); 
Constraints:
- 
     is_same_v < remove_cvref_t < Err > , unexpected > false; and
- 
     is_same_v < remove_cvref_t < Err > , in_place_t > false; and
- 
     is_constructible_v < E , Err > true.
Effects: Direct-non-list-initializes 
Throws: Any exception thrown by the initialization of 
template < class ... Args > constexpr explicit unexpected ( in_place_t , Args && ...); 
Constraints: true.
Effects: Direct-non-list-initializes 
Throws: Any exception thrown by the initialization of 
template < class U , class ... Args > constexpr explicit unexpected ( in_place_t , initializer_list < U > , Args && ...); 
Constraints: true.
Effects: Direct-non-list-initializes 
Throws: Any exception thrown by the initialization of 
5.3.2. �.�.3.2.3 Observers [expected.un.observe]
constexpr const E & value () const & noexcept ; constexpr E & value () & noexcept ; 
Returns: 
constexpr E && value () && noexcept ; constexpr const E && value () const && noexcept ; 
Returns: 
5.3.3. �.�.3.2.4 Swap [expected.un.swap]
constexpr void swap ( unexpected & other ) noexcept ( is_nothrow_swappable_v < E > ); 
Mandates: true.
Effects: Equivalent to 
friend constexpr void swap ( unexpected & x , unexpected & y ) noexcept ( noexcept ( x . swap ( y ))); 
Constraints: true.
Effects: Equivalent to 
5.4. �.�.3.2.5 Equality operators [expected.un.eq]
template < class E2 > friend constexpr bool operator == ( const unexpected & x , const unexpected < E2 >& y ); 
Mandates: The expression 
Returns: 
5.5. �.�.4 Class template bad_expected_access 
template < class E > class bad_expected_access : public bad_expected_access < void > { public : explicit bad_expected_access ( E ); const char * what () const noexcept override ; E & error () & noexcept ; const E & error () const & noexcept ; E && error () && noexcept ; const E && error () const && noexcept ; private : E val ; // exposition only }; 
The template class false.
explicit bad_expected_access ( E e ); 
Effects: Initializes 
const E & error () const & noexcept ; E & error () & noexcept ; 
Returns: 
E && error () && noexcept ; const E && error () const && noexcept ; 
Returns: 
const char * what () const noexcept override ; 
Returns: An implementation-defined NTBS.
5.6. �.�.5 Class template specialization bad_expected_access < void > 
template <> class bad_expected_access < void > : public exception { protected : bad_expected_access () noexcept ; bad_expected_access ( const bad_expected_access & ); bad_expected_access ( bad_expected_access && ); bad_expected_access & operator = ( const bad_expected_access & ); bad_expected_access & operator = ( bad_expected_access && ); ~ bad_expected_access (); public : const char * what () const noexcept override ; }; 
const char * what () const noexcept override ; 
Returns: An implementation-defined NTBS.
5.7. �.�.7 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 > ; // �.�.7.1, constructors constexpr expected (); constexpr explicit ( see below ) expected ( const expected & ); constexpr explicit ( see below ) expected ( expected && ) noexcept ( see below ); template < class U , class G > constexpr explicit ( see below ) expected ( const expected < U , G >& ); template < class U , class G > constexpr explicit ( see below ) expected ( expected < U , G >&& ); template < class U = T > constexpr explicit ( see below ) expected ( U && v ); template < class G > constexpr expected ( const unexpected < G >& ); template < class G > 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 && ...); // �.�.7.2, destructor constexpr ~ expected (); // �.�.7.3, assignment constexpr expected & operator = ( const expected & ); constexpr expected & operator = ( expected && ) noexcept ( see below ); template < class U = T > constexpr expected & operator = ( U && ); template < class G > constexpr expected & operator = ( const unexpected < G >& ); template < class G > constexpr expected & operator = ( unexpected < G >&& ); // �.�.7.4, modifiers template < class ... Args > constexpr T & emplace ( Args && ...) noexcept ; template < class U , class ... Args > constexpr T & emplace ( initializer_list < U > , Args && ...) noexcept ; // �.�.7.5, swap constexpr void swap ( expected & ) noexcept ( see below ); // �.�.7.6, observers constexpr const T * operator -> () const noexcept ; constexpr T * operator -> () noexcept ; constexpr const T & operator * () const & noexcept ; constexpr T & operator * () & noexcept ; constexpr const T && operator * () const && noexcept ; constexpr T && operator * () && noexcept ; 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 && ) && ; // �.�.7.7, Expected equality operators template < class T2 , class E2 > friend constexpr bool operator == ( const expected & x , const expected < T2 , E2 >& y ); template < class T2 > friend constexpr bool operator == ( const expected & , const T2 & ); template < class E2 > friend constexpr bool operator == ( const expected & , const unexpected < E2 >& ); // �.�.7.10, Specialized algorithms friend constexpr void swap ( expected & , expected & ) noexcept ( see below ); private : bool has_val ; // exposition only union { T val ; // exposition only E unex ; // exposition only }; }; 
Any object of 
A program that instantiates the definition of template 
When 
5.8. �.�.7.1 Constructors [expected.object.ctor]
constexpr expected (); 
Constraints: true.
Effects: Value-initializes 
Postconditions: true.
Throws: Any exception thrown by the initialization of 
constexpr expected ( const expected & rhs ); 
Effects: If true, direct-non-list-initializes 
Postconditions: 
Throws: Any exception thrown by the initialization of 
Remarks: This constructor is defined as deleted unless:
- 
     is_copy_constructible_v < T > true; and
- 
     is_copy_constructible_v < E > true.
This constructor is trivial if:
- 
     is_trivially_copy_constructible_v < T > true; and
- 
     is_trivially_copy_constructible_v < E > true.
constexpr expected ( expected && rhs ) noexcept ( see below ); 
Constraints:
- 
     is_move_constructible_v < T > true; and
- 
     is_move_constructible_v < E > true.
Effects: If true, direct-non-list-initializes 
Postconditions: 
Throws: Any exception thrown by the initialization of 
Remarks:
The exception specification is 
Remarks: This constructor is trivial if:
- 
     is_trivially_move_constructible_v < T > true; and
- 
     is_trivially_move_constructible_v < E > true.
template < class U , class G > constexpr explicit ( see below ) expected ( const expected < U , G >& rhs ); template < class U , class G > constexpr explicit ( see below ) expected ( expected < U , G >&& rhs ); 
Let:
- 
     UF const U & U 
- 
     GF const G & G 
Constraints:
- 
     is_constructible_v < T , UF > true; and
- 
     is_constructible_v < E , GF > true; and
- 
     is_constructible_v < T , expected < U , G >&> false; and
- 
     is_constructible_v < T , expected < U , G >> false; and
- 
     is_constructible_v < T , const expected < U , G >&> false; and
- 
     is_constructible_v < T , const expected < U , G >> false; and
- 
     is_convertible_v < expected < U , G >& , T > false; and
- 
     is_convertible_v < expected < U , G >&& , T > false; and
- 
     is_convertible_v < const expected < U , G >& , T > false; and
- 
     is_convertible_v < const expected < U , G >&& , T > false; and
- 
     is_constructible_v < unexpected < E > , expected < U , G >&> false; and
- 
     is_constructible_v < unexpected < E > , expected < U , G >> false; and
- 
     is_constructible_v < unexpected < E > , const expected < U , G >&> false; and
- 
     is_constructible_v < unexpected < E > , const expected < U , G >> false.
Effects: If 
Postconditions: 
Throws: Any exception thrown by the initialization of 
Remarks:
The expression inside 
template < class U = T > constexpr explicit ( ! is_convertible_v < U , T > ) expected ( U && v ); 
Constraints:
- 
     is_same_v < remove_cvref_t < U > , in_place_t > false; and
- 
     is_same_v < expected < T , E > , remove_cvref_t < U >> false; and
- 
     remove_cvref_t < U > unexpected 
- 
     is_constructible_v < T , U > true.
Effects: Direct-non-list-initializes 
Postconditions: true.
Throws: Any exception thrown by the initialization of 
template < class G > constexpr explicit ( ! is_convertible_v < const G & , E > ) expected ( const unexpected < G >& e ); template < class G > constexpr explicit ( ! is_convertible_v < G , E > ) expected ( unexpected < G >&& e ); 
Let 
Constraints: true.
Effects: Direct-non-list-initializes 
Postconditions: false.
Throws: Any exception thrown by the initialization of 
template < class ... Args > constexpr explicit expected ( in_place_t , Args && ... args ); 
Constraints:
- 
     is_constructible_v < T , Args ... > true.
Effects: Direct-non-list-initializes 
Postconditions: true.
Throws: Any exception thrown by the initialization of 
template < class U , class ... Args > constexpr explicit expected ( in_place_t , initializer_list < U > il , Args && ... args ); 
Constraints: true.
Effects: Direct-non-list-initializes 
Postconditions: true.
Throws: Any exception thrown by the initialization of 
template < class ... Args > constexpr explicit expected ( unexpect_t , Args && ... args ); 
Constraints: true.
Effects: Direct-non-list-initializes 
Postconditions: false.
Throws: Any exception thrown by the initialization of 
template < class U , class ... Args > constexpr explicit expected ( unexpect_t , initializer_list < U > il , Args && ... args ); 
Constraints: true.
Effects: Direct-non-list-initializes 
Postconditions: false.
Throws: Any exception thrown by the initialization of 
5.9. �.�.7.2 Destructor [expected.object.dtor]
constexpr ~ expected (); 
Effects: If true, destroys 
Remarks: If true, and true, then this destructor
is a trivial destructor.
5.10. �.�.7.3 Assignment [expected.object.assign]
This subclause makes use of the following exposition-only function:
 [Drafting note: the name reinit - expected 
template < class T , class U , class ... Args > constexpr void reinit - expected ( T & newval , U & oldval , Args && ... args ) { if constexpr ( is_nothrow_constructible_v < T , Args ... > ) { destroy_at ( addressof ( oldval )); construct_at ( addressof ( newval ), std :: forward < Args > ( args )...); } else if constexpr ( is_nothrow_move_constructible_v < T > ) { T tmp ( std :: forward < Args > ( args )...); destroy_at ( addressof ( oldval )); construct_at ( addressof ( newval ), std :: move ( tmp )); } else { U tmp ( std :: move ( oldval )); destroy_at ( addressof ( oldval )); try { construct_at ( addressof ( newval ), std :: forward < Args > ( args )...); } catch (...) { construct_at ( addressof ( oldval ), std :: move ( tmp )); throw ; } } } 
constexpr expected & operator = ( const expected & rhs ); 
Effects:
- 
     If this -> has_value () && rhs . has_value () true, equivalent toval = * rhs 
- 
     Otherwise, if this -> has_value () true, equivalent toreinit - expected ( unex , val , rhs . error ()) 
- 
     Otherwise, if rhs . has_value () true, equivalent toreinit - expected ( val , unex , * rhs ) 
- 
     Otherwise, equivalent to unex = rhs . error () 
Then, if no exception was thrown, equivalent to: 
Remarks: This operator is defined as deleted unless:
- 
     is_copy_assignable_v < T > trueandis_copy_constructible_v < T > trueandis_copy_assignable_v < E > trueandis_copy_constructible_v < E > trueandis_nothrow_move_constructible_v < E > || is_nothrow_move_constructible_v < T > true.
constexpr expected & operator = ( expected && rhs ) noexcept ( see below ); 
Constraints: true and true and true and true and true.
Effects:
- 
     If this -> has_value () && rhs . has_value () true, equivalent toval = std :: move ( * rhs ) 
- 
     Otherwise, if this -> has_value () true, equivalent toreinit - expected ( unex , val , std :: move ( rhs . error ())) 
- 
     Otherwise, if rhs . has_value () true, equivalent toreinit - expected ( val , unex , std :: move ( * rhs )) 
- 
     Otherwise, equivalent to unex = std :: move ( rhs . error ()) 
Then, if no exception was thrown, equivalent to: 
Remarks:
The exception specification is 
template < class U = T > constexpr expected & operator = ( U && v ); 
Constraints:
- 
     is_same_v < expected , remove_cvref_t < U >> false; and
- 
     remove_cvref_t < U > unexpected 
- 
     is_constructible_v < T , U > true; and
- 
     is_assignable_v < T & , U > true; and
- 
     is_nothrow_constructible_v < T , U > || is_nothrow_move_constructible_v < T > || is_nothrow_move_constructible_v < E > true.
Effects:
- 
     If has_value () true, equivalent to:val = std :: forward < U > ( v ); return * this ; 
- 
     Otherwise, equivalent to reinit - expected ( val , unex , std :: forward < U > ( v )); has_val = true; return * this ; 
template < class G > constexpr expected & operator = ( const unexpected < G >& e ); template < class G > constexpr expected & operator = ( unexpected < G >&& e ); 
Let 
Constraints:
- 
     is_constructible_v < E , GF > true.
- 
     is_assignable_v < E & , GF > true; and
- 
     is_nothrow_constructible_v < E , GF > || is_nothrow_move_constructible_v < T > || is_nothrow_move_constructible_v < E > true.
Effects:
- 
     If has_value () true, equivalent toreinit - expected ( unex , val , std :: forward < GF > ( e . value ())); has_val = false; return * this ; 
- 
     Otherwise, equivalent to: unex = std :: forward < GF > ( e . value ()); return * this ; 
template < class ... Args > constexpr T & emplace ( Args && ... args ) noexcept ; 
Constraints: true.
Effects: Equivalent to:
if ( has_value ()) destroy_at ( addressof ( val )); else { destroy_at ( addressof ( unex )); has_val = true; } return * construct_at ( addressof ( val ), std :: forward < Args > ( args )...); 
template < class U , class ... Args > constexpr T & emplace ( initializer_list < U > il , Args && ... args ) noexcept ; 
Constraints: true.
Effects: Equivalent to:
if ( has_value ()) destroy_at ( addressof ( val )); else { destroy_at ( addressof ( unex )); has_val = true; } return * construct_at ( addressof ( val ), il , std :: forward < Args > ( args )...); 
5.11. �.�.7.4 Swap [expected.object.swap]
constexpr void swap ( expected & rhs ) noexcept ( see below ); 
Constraints:
- 
     is_swappable_v < T > 
- 
     is_swappable_v < E > 
- 
     is_move_constructible_v < T > && is_move_constructible_v < E > true, and
- 
     is_nothrow_move_constructible_v < T > || is_nothrow_move_constructible_v < E > true.
Effects: See Table editor-please-pick-a-number-5
|  |  | |
|  | equivalent to:  | calls  | 
|  | See below †. | equivalent to:  | 
† For the case where false and true, equivalent to:
if constexpr ( is_nothrow_move_constructible_v < E > ) { E tmp ( std :: move ( rhs . unex )); destroy_at ( addressof ( rhs . unex )); try { construct_at ( addressof ( rhs . val ), std :: move ( val )); destroy_at ( addressof ( val )); construct_at ( addressof ( unex ), std :: move ( tmp )); } catch (...) { construct_at ( addressof ( rhs . unex ), std :: move ( tmp )); throw ; } else { T tmp ( std :: move ( val )); destroy_at ( addressof ( val )); try { construct_at ( addressof ( unex ), std :: move ( rhs . unex )); destroy_at ( addressof ( rhs . unex )); construct_at ( addressof ( rhs . val ), std :: move ( tmp )); } catch (...) { construct_at ( addressof ( val ), std :: move ( tmp )); throw ; } } has_val = false; rhs . has_val = true; 
Throws: Any exception thrown by the expressions in the Effects.
Remarks: The exception specification is:
is_nothrow_move_constructible_v < T > && is_nothrow_swappable_v < T > && is_nothrow_move_constructible_v < E > && is_nothrow_swappable_v < E > 
friend constexpr void swap ( expected & x , expected & y ) noexcept ( noexcept ( x . swap ( y ))); 
Effects: Equivalent to 
5.12. �.�.7.5 Observers [expected.object.observe]
constexpr const T * operator -> () const noexcept ; constexpr T * operator -> () noexcept ; 
Preconditions: true.
Returns: 
constexpr const T & operator * () const & noexcept ; constexpr T & operator * () & noexcept ; 
Preconditions: true.
Returns: 
constexpr T && operator * () && noexcept ; constexpr const T && operator * () const && noexcept ; 
Preconditions: true.
Returns: 
constexpr explicit operator bool () const noexcept ; constexpr bool has_value () const noexcept ; 
Returns: 
constexpr const T & value () const & ; constexpr T & value () & ; 
Returns: true .
Throws: false.
constexpr T && value () && ; constexpr const T && value () const && ; 
Returns: true .
Throws: false.
constexpr const E & error () const & noexcept ; constexpr E & error () & noexcept ; 
Preconditions: false.
Returns: 
constexpr E && error () && noexcept ; constexpr const E && error () const && noexcept ; 
Preconditions: false.
Returns: 
template < class U > constexpr T value_or ( U && v ) const & ; 
Mandates: true and true.
Returns: 
template < class U > constexpr T value_or ( U && v ) && ; 
Mandates: true and true.
Returns: 
5.13. �.�.7.6 Expected Equality operators [expected.object.eq]
template < class T2 , class E2 > requires ( ! is_void_v < T2 > ) friend constexpr bool operator == ( const expected & x , const expected < T2 , E2 >& y ); 
Mandates: The expressions 
Returns:
If false;
otherwise if true, 
template < class T2 > constexpr bool operator == ( const expected & x , const T2 & v ); 
Mandates: The expression 
Returns: 
template < class E2 > constexpr bool operator == ( const expected & x , const unexpected < E2 >& e ); 
Mandates: The expression 
Returns: 
5.14. �.�.8 Partial specialization of expected for void types [expected.void]
template < class T , class E > requires is_void_v < T > class expected < T , E > { public : using value_type = T ; using error_type = E ; using unexpected_type = unexpected < E > ; template < class U > using rebind = expected < U , error_type > ; // �.�.8.1, constructors constexpr expected () noexcept ; constexpr explicit ( see below ) expected ( const expected & ); constexpr explicit ( see below ) expected ( expected && ) noexcept ( see below ); template < class U , class G > constexpr explicit ( see below ) expected ( const expected < U , G >& ); template < class U , class G > constexpr explicit ( see below ) expected ( expected < U , G >&& ); template < class G > constexpr expected ( const unexpected < G >& ); template < class G > constexpr expected ( unexpected < G >&& ); constexpr explicit expected ( in_place_t ) noexcept ; template < class ... Args > constexpr explicit expected ( unexpect_t , Args && ...); template < class U , class ... Args > constexpr explicit expected ( unexpect_t , initializer_list < U > , Args && ...); // �.�.8.2, destructor constexpr ~ expected (); // �.�.8.3, assignment constexpr expected & operator = ( const expected & ); constexpr expected & operator = ( expected && ) noexcept ( see below ); template < class G > constexpr expected & operator = ( const unexpected < G >& ); template < class G > constexpr expected & operator = ( unexpected < G >&& ); // �.�.8.4, modifiers constexpr void emplace () noexcept ; // �.�.8.5, swap constexpr void swap ( expected & ) noexcept ( see below ); // �.�.8.6, observers constexpr explicit operator bool () const noexcept ; constexpr bool has_value () const noexcept ; constexpr void operator * () const noexcept ; constexpr void value () const & ; constexpr void value () && ; constexpr const E & error () const & ; constexpr E & error () & ; constexpr const E && error () const && ; constexpr E && error () && ; // �.�.8.7, Expected equality operators template < class T2 , class E2 > requires is_void_v < T2 > friend constexpr bool operator == ( const expected & x , const expected < T2 , E2 >& y ); template < class E2 > friend constexpr bool operator == ( const expected & , const unexpected < E2 >& ); // �.�.8.10, Specialized algorithms friend constexpr void swap ( expected & , expected & ) noexcept ( see below ); private : bool has_val ; // exposition only union { E unex ; // exposition only }; }; 
5.15. �.�.8.1 Constructors [expected.void.ctor]
constexpr expected () noexcept ; 
Postconditions: true.
constexpr expected ( const expected & rhs ); 
Effects: If false, direct-non-list-initializes 
Postconditions: 
Throws: Any exception thrown by the initialization of 
Remarks: This constructor is defined as deleted unless true.
This constructor is trivial if true.
constexpr expected ( expected && rhs ) noexcept ( is_nothrow_move_constructible_v < E > ); 
Constraints: true.
Effects: If false, direct-non-list-initializes 
Postconditions: 
Throws: Any exception thrown by the initialization of 
Remarks: This constructor is trivial if true.
template < class U , class G > constexpr explicit ( ! is_convertible_v < const G & , E > ) expected ( const expected < U , G >& rhs ); template < class U , class G > constexpr explicit ( ! is_convertible_v < G , E > ) expected ( expected < U , G >&& rhs ); 
Let 
Constraints:
- 
     is_void_v < U > true; and
- 
     is_constructible_v < E , GF > true; and
- 
     is_constructible_v < unexpected < E > , expected < U , G >&> false; and
- 
     is_constructible_v < unexpected < E > , expected < U , G >> false; and
- 
     is_constructible_v < unexpected < E > , const expected < U , G >&> false; and
- 
     is_constructible_v < unexpected < E > , const expected < U , G >> false; and
Effects: If false, direct-non-list-initializes 
Postconditions: 
Throws: Any exception thrown by the initialization of 
template < class G > constexpr explicit ( ! is_convertible_v < const G & , E > ) expected ( const unexpected < G >& e ); template < class G > constexpr explicit ( ! is_convertible_v < G , E > ) expected ( unexpected < G >&& e ); 
Let 
Constraints: true.
Effects: Direct-non-list-initializes 
Postconditions: false.
Throws: Any exception thrown by the initialization of 
constexpr explicit expected ( in_place_t ) noexcept ; 
Postconditions: true.
template < class ... Args > constexpr explicit expected ( unexpect_t , Args && ... args ); 
Constraints: true.
Effects: Direct-non-list-initializes 
Postconditions: false.
Throws: Any exception thrown by the initialization of 
template < class U , class ... Args > constexpr explicit expected ( unexpect_t , initializer_list < U > il , Args && ... args ); 
Constraints: true.
Effects: Direct-non-list-initializes 
Postconditions: false.
Throws: Any exception thrown by the initialization of 
5.16. �.�.8.2 Destructor [expected.void.dtor]
constexpr ~ expected (); 
Effects: If false, destroys 
Remarks: If true, then this destructor
is a trivial destructor.
5.17. �.�.8.3 Assignment [expected.void.assign]
constexpr expected & operator = ( const expected & rhs ); 
Effects:
- 
     If this -> has_value () && rhs . has_value () true, no effects.
- 
     Otherwise, if this -> has_value () true, equivalent to:construct_at ( addressof ( unex ), rhs . unex ); has_val = false; 
- 
     Otherwise, if rhs . has_value () true, destroysunex has_val true.
- 
     Otherwise, equivalent to unex = rhs . error () 
Returns: 
Remarks: This operator is defined as deleted unless:
- 
     is_copy_assignable_v < E > trueandis_copy_constructible_v < E > true.
constexpr expected & operator = ( expected && rhs ) noexcept ( see below ); 
Effects:
- 
     If this -> has_value () && rhs . has_value () true, no effects.
- 
     Otherwise, if this -> has_value () true, equivalent to:construct_at ( addressof ( unex ), std :: move ( rhs . unex )); has_val = false; 
- 
     Otherwise, if rhs . has_value () true, destroysunex has_val true.
- 
     Otherwise, equivalent to unex = rhs . error () 
Returns: 
Remarks: The exception specification is 
This operator is defined as deleted unless:
- 
     is_move_constructible_v < E > trueandis_move_assignable_v < E > true.
template < class G > constexpr expected & operator = ( const unexpected < G >& e ); template < class G > constexpr expected & operator = ( unexpected < G >&& e ); 
Let 
Constraints: true and true.
Effects:
- 
     If has_value () true, equivalent to:construct_at ( addressof ( unex ), std :: forward < GF > ( e . value ())); has_val = false; return * this ; 
- 
     Otherwise, equivalent to: unex = std :: forward < GF > ( e . value ()); return * this ; 
constexpr void emplace () noexcept ; 
Effects:
If false, destroys true.
5.18. �.�.8.4 Swap [expected.void.swap]
constexpr void swap ( expected & rhs ) noexcept ( see below ); 
Constraints: true; and true.
Effects: See Table editor-please-pick-a-number-5
|  |  | |
|  | no effects | calls  | 
|  | See below †. | equivalent to:  | 
† For the case where false and true, equivalent to:
construct_at ( addressof ( unex ), std :: move ( rhs . unex )); destroy_at ( addressof ( rhs . unex )); has_val = false; rhs . has_val = true; 
Throws: Any exception thrown by the expressions in the Effects.
Remarks: The exception specification is 
friend constexpr void swap ( expected & x , expected & y ) noexcept ( noexcept ( x . swap ( y ))); 
Effects: Equivalent to 
5.19. �.�.8.5 Observers [expected.void.observe]
constexpr explicit operator bool () const noexcept ; constexpr bool has_value () const noexcept ; 
Returns: 
constexpr void operator * () const noexcept ; 
Preconditions: true.
constexpr void value () const & ; 
Throws: false.
constexpr void value () && ; 
Throws: false.
constexpr const E & error () const & ; constexpr E & error () & ; 
Preconditions: false.
Returns: 
constexpr E && error () && ; constexpr const E && error () const && ; 
Preconditions: false.
Returns: 
5.20. �.�.8.6 Expected Equality operators [expected.void.eq]
template < class T2 , class E2 > requires is_void_v < T2 > friend constexpr bool operator == ( const expected & x , const expected < T2 , E2 >& y ); 
Mandates: The expression 
Returns:
If false;
otherwise 
template < class E2 > constexpr bool operator == ( const expected & x , const unexpected < E2 >& e ); 
Mandates: The expression 
Returns: 
5.21. 16.4.5.3.2 Zombie names [zombie.names]
Remove 
In namespace, the following names are reserved for previous standardization:std 
- [...],
,undeclare_no_pointers 
, andundeclare_reachable 
, andunexpected 
.unexpected_handler 
6. Implementation & Usage Experience
There are multiple implementations of 
6.1. Sy Brand
By far the most popular implementation is Sy Brand’s, with over 500 stars on GitHub and extensive usage.
- Code: https://github.com/TartanLlama/expected
- 
     Non comprehensive usage list: 
     - Telegram desktop client
- Ceph distributed storage system
- FiveM and RedM mod frameworks
- Rspamd spam filtering system
- OTTO hardware synth
- Some NIST project
- about 10 cryptocurrency projects
 
- 
     Testimonials: 
     - 
       https://twitter.com/syoyo/status/1328196033545814016 
       Ultra super cooooooooooooooooooooooooooooooooopooool!!!!! 🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🙏🙏🙏🙏🙏🙏🙏🙏🙏☺️☺️☺️☺️☺️☺️☺️☺️🥰🥰🥰🥰🥰🥰🥰😍😍😍😍😍😍😍😍😍 C++11/14/17 std::expected with functional-style extensions 
- 
       https://twitter.com/LesleyLai6/status/1328199023786770432 
       I use @TartanLlama 's optional and expected libraries for almost all of my projects. Thay are amazing! 
 Though I made a custom fork and added a few rust Rusult like features.
- 
       https://twitter.com/bjorn_fahller/status/1229803982685638656 
       I used tl::expected<> and a few higher order functions to extend functionality with 1/3 code size ;-) 
- 
       https://twitter.com/toffiloff/status/1101559543631351808 
       Also, using @TartanLlama’s ‘expected’ library has done wonders for properly handling error cases on bare metal systems without exceptions enabled 
- 
       https://twitter.com/chsiedentop/status/1296624103080640513 
       I can really recommend the tl::expected library which has this 😉. BTW, great library, @TartanLlama! 
 
- 
       https://twitter.com/syoyo/status/1328196033545814016 
       
6.2. Vicente J. Botet Escriba
The original author of 
6.3. WebKit
The WebKit web browser (used in Safari) contains an implementation that’s used throughout its codebase.