C++
std::expected
Learn error handling with std::expected.
What is std::expected?
std::expected<T, E> is a type introduced with C++23: it holds either a T value or an E error. Instead of throwing an exception, it returns the error as a value.
Example: std::expected<int, std::string> divide(int a, int b). On success return a / b;, on error return std::unexpected(std::string("division by zero"));. The caller checks the state with r.has_value(), gets the value with r.value() and the error with r.error(). This provides error handling without the cost of exceptions; it is especially useful to separate real exceptions from expected failures.
value() and error()
From a std::expected object you get the value with .value() and the error with .error(). You must check .has_value() first.
Example: auto r = divide(10, 2); if (r.has_value()) std::cout << r.value(); else std::cout << r.error();. If you call .value() when there is no value, an exception is thrown. So you must check has_value() first. Errors are produced with std::unexpected: return std::unexpected(std::string("error"));. This is an explicit and type-safe error handling.
Exercise Write divide with std::expected
Write std::expected<int, std::string> divide(int a, int b): if b == 0 return std::unexpected(std::string("division by zero"));, otherwise return a / b;. In main call divide(10, 2); if has_value() is true print value(), otherwise print error() with std::cout.
Show example solution
#include <iostream>
#include <expected>
#include <string>
std::expected<int, std::string> divide(int a, int b) {
if (b == 0) return std::unexpected(std::string("division by zero"));
return a / b;
}
int main() {
auto r = divide(10, 2);
if (r.has_value()) std::cout << r.value();
else std::cout << r.error();
return 0;
}
Test yourself
-
When should std::expected be used?
- Always instead of exceptions
- For multithreading
- Only for memory errors
- To return expected error conditions as values
Show answer
To return expected error conditions as values — std::expected returns expected errors as values instead of exceptions.
-
Match each member to its role
- has_value
- Has a value?
- value()
- Get the value
- error()
- Get the error
- std::unexpected
- Produces an error
-
the .value() call
if (r.has_value()) std::cout << r.____();
Show answer
if (r.has_value()) std::cout << r.value();
Next lesson: noexcept