When I looked at the compiler support for cppreference recently, I found that libstdc++ already implements <expected>, but unfortunately I didn’t find much information about it on the web.

What is std::expected?

It is similar to std::optional, but std::optional can only indicate a normal value or std::nullopt, i.e. a null value. In contrast, std::expected can indicate an expected value and an error value, which is equivalent to the two-member std::variant, but is more convenient to use on the interface. Think of it as a new kind of error handling.

Basic use

Constructs

std::expected<T,E> has two template arguments, the first indicating the expected value and the second indicating the wrong value.

If it is the expected value, there is an implicit conversion.

 1  std::expected e = 42; 

If it is an exception, it is initialized by std::expected().

 1  std::expected e = std::unexpected("Error"); 

default constructor

 1  std::expected e; 

Requires S to have a default constructor, otherwise an exception will be thrown.

Usage

Same pointer semantics as std::optional, can be dereferenced.

 1 2 3  std::expected e = 42; if (e) std::cout << *e << "\n"; // prints 42 

Note that you must check before unquoting, otherwise it is UB!!!

The following is equivalent in effect to the above.

 1 2 3  std::expected e = 42; if (e.has_value()) std::cout << e.value() << "\n"; // prints 42 

Note that std::expected<T,E>::value() throws an exception if the value does not exist, which is somewhat safer.

Indicates an error

 1 2 3  std::expected e = std::unexpected("Error"); if (!e.has_value()) std::cout << e.error() << "\n"; // prints Error 

If not used incorrectly as UB!!!

If the result returned is said to be an error, a default value can be provided more elegantly with value_or().

  1 2 3 4 5 6 7 8 9 10 11  std::expected MayHasErr(int i) { if (i < 0) return std::unexpected("Error"); else return i * 2; } int main() { std::cout << MayHasErr(3).value() << "\n"; // print 3 std::cout << MayHasErr(-2).value_or(42) << "\n"; // print 42 } 

Internal implementation

The implementation of std::expected<T,E> is relatively simple and does not rely on any new language features. It consists of an expected value of type T and a union of std::unexpected<E> and a bool value indicating whether it is an error or not. The rest are some helper functions.

  1 2 3 4 5 6 7 8 9 10  template class expected { using unexpected_type = unexpected; private: bool has_val; union { T val; // 表示期望的值 E unex; // 表示异常的值 }; };