Skip to content

std::expected (C++23) ​

In a Nutshell ​

Holds either the expected normal value T or an unexpected error E — a type-safe, zero-overhead error-propagation mechanism that replaces exceptions and the std::pair<T, Error> pattern.

#include <expected>

Core API Cheat Sheet ​

OperationSignatureDescription
Construct (success value)expected(T value)Wraps a normal value
Construct (error)expected(unexpect_t, E err)Wraps an error (std::unexpected{err})
Check for successbool has_value() const noexceptWhether a normal value is held
Implicit bool conversionexplicit operator bool() const noexceptSame as has_value
Get the valueT& value()Returns a reference to the normal value (throws on failure)
Get the errorconst E& error() constReturns a reference to the error
DereferenceT& operator*()Accesses the normal value (unchecked; undefined behavior if it holds an error)
Chained transformauto transform(F&& f)If a value is held, applies f to it and wraps the result
Chained error handlingauto and_then(F&& f)If a value is held, calls f and returns its expected result
Error branchauto or_else(F&& f)If an error is held, calls f to handle it
Error transformauto transform_error(F&& f)If an error is held, applies f to the error
Create a success valuestd::expected<T, E>(value)Factory: directly constructs a success
Create an error valuestd::unexpected{err}Factory: constructs an unexpected for implicit conversion to expected

Minimal Example ​

Expand codeCollapse21 lines
C++
// Standard: C++23
#include <expected>
#include <iostream>
#include <string>

std::expected<int, std::string> divide(int a, int b) {
    if (b == 0) return std::unexpected{"division by zero"};
    return a / b;
}

int main() {
    auto r1 = divide(10, 3);
    if (r1) std::cout << *r1 << "\n"; // 3

    auto r2 = divide(10, 0);
    if (!r2) std::cout << r2.error() << "\n"; // division by zero

    // Chained calls
    auto r3 = divide(20, 4).transform([](int v) { return v * 2; });
    std::cout << *r3 << "\n"; // 10
}

Embedded Applicability: High ​

  • A zero-overhead abstraction: its size is sizeof(T) + sizeof(E) plus a discriminant flag, with no heap allocation
  • Replaces the exception-handling mechanism, making it a good fit for embedded environments where exceptions are disabled (-fno-exceptions)
  • More type-safe than the error code + output parameter pattern, forcing callers to handle errors
  • Chained operations (transform/and_then) can compose complex workflows while keeping the code linear and readable

Compiler Support ​

GCCClangMSVC
121619.36

See Also ​


Part of the content references cppreference.com, licensed under CC-BY-SA 4.0

pdf-latest-4-g85128cc · 85128cc · 2026-10-05