CodeNebula
C++23stlexpert

std::expected (C++23 Addition)

C++23 · Completing Modern · Compiler flag: -std=c++23

Explanation

Represents a value or an error, safer than exceptions for error handling.

Real-World Scenario

Handle file operation results in a file manager.

Quick Snippet

snippet.cpp
#include <expected> std::expected<int, std::string> result = 42;

Full Example

expected.cpp
// CodeNebula - C++23 Example: std::expected
// Compile: g++ -std=c++23 -Wall -Wextra -o example expected.cpp
// Note: Requires GCC 12+ / Clang 16+ / MSVC 19.33+

// std::expected<T, E> holds either a value of type T or an error of type E.
// It provides a monadic, exception-free error handling mechanism.
// Similar to Rust's Result<T, E>.

#include <iostream>
#include <expected>
#include <string>
#include <cmath>
#include <charconv>

enum class MathError { DivisionByZero, NegativeSqrt, Overflow };

std::string to_string(MathError e) {
    switch (e) {
        case MathError::DivisionByZero: return "division by zero";
        case MathError::NegativeSqrt:   return "sqrt of negative";
        case MathError::Overflow:       return "overflow";
    }
    return "unknown";
}

// Return expected instead of throwing
std::expected<double, MathError> safe_divide(double a, double b) {
    if (b == 0.0) return std::unexpected(MathError::DivisionByZero);
    return a / b;
}

std::expected<double, MathError> safe_sqrt(double x) {
    if (x < 0.0) return std::unexpected(MathError::NegativeSqrt);
    return std::sqrt(x);
}

// Parse integer from string
std::expected<int, std::string> parse_int(std::string_view sv) {
    int result = 0;
    auto [ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), result);
    if (ec != std::errc{}) return std::unexpected("invalid integer: " + std::string(sv));
    return result;
}

int main() {
    std::cout << "=== std::expected ===" << std::endl;

    // Basic usage
    auto r1 = safe_divide(10.0, 3.0);
    auto r2 = safe_divide(10.0, 0.0);

    std::cout << "10 / 3 = ";
    if (r1) std::cout << r1.value() << std::endl;
    else    std::cout << "Error: " << to_string(r1.error()) << std::endl;

    std::cout << "10 / 0 = ";
    if (r2) std::cout << r2.value() << std::endl;
    else    std::cout << "Error: " << to_string(r2.error()) << std::endl;

    // value_or: provide default on error
    std::cout << "\n=== value_or ===" << std::endl;
    std::cout << "sqrt(25) or -1: " << safe_sqrt(25).value_or(-1) << std::endl;
    std::cout << "sqrt(-4) or -1: " << safe_sqrt(-4).value_or(-1) << std::endl;

    // Monadic operations: and_then, transform, or_else
    std::cout << "\n=== Monadic chaining ===" << std::endl;
    auto result = safe_divide(100.0, 4.0)
        .and_then([](double v) { return safe_sqrt(v); })
        .transform([](double v) { return v * 2; });

    std::cout << "sqrt(100/4) * 2 = " << result.value() << std::endl;

    // Chain that produces an error
    auto bad = safe_divide(100.0, 0.0)
        .and_then([](double v) { return safe_sqrt(v); })
        .transform([](double v) { return v * 2; });

    std::cout << "Error chain: " << to_string(bad.error()) << std::endl;

    // Parsing example
    std::cout << "\n=== Parsing ===" << std::endl;
    for (auto s : {"42", "abc", "100"}) {
        auto parsed = parse_int(s);
        if (parsed) std::cout << "'" << s << "' -> " << *parsed << std::endl;
        else        std::cout << "'" << s << "' -> Error: " << parsed.error() << std::endl;
    }

    return 0;
}

/*
Expected output:
=== std::expected ===
10 / 3 = 3.33333
10 / 0 = Error: division by zero

=== value_or ===
sqrt(25) or -1: 5
sqrt(-4) or -1: -1

=== Monadic chaining ===
sqrt(100/4) * 2 = 10
Error chain: division by zero

=== Parsing ===
'42' -> 42
'abc' -> Error: invalid integer: abc
'100' -> 100
*/
Run command
g++ -std=c++23 -Wall -Wextra -o example expected.cpp