Daily C++ Tip 1

Return errors as values with std::expected

Every fallible step returns std::expected, and the monadic chain passes errors through untouched.

std::expected<T, E> carries either a value or an error, so error paths stay visible in the type instead of hiding in exceptions or out-parameters. transform maps the value and passes errors through untouched.

#include <expected>
#include <iostream>
#include <string>

std::expected<int, std::string> parse(const std::string& s) {
    try {
        size_t pos = 0;
        int v = std::stoi(s, &pos);
        if (pos != s.size()) return std::unexpected("trailing characters");
        return v;
    } catch (...) {
        return std::unexpected("not a number");
    }
}

int main() {
    auto ok = parse("21").transform([](int v) { return v * 2; });
    std::cout << (ok ? std::to_string(*ok) : "error: " + ok.error()) << '\n';
    auto bad = parse("21x").transform([](int v) { return v * 2; });
    std::cout << (bad ? std::to_string(*bad) : "error: " + bad.error()) << '\n';
}

Observed output:

42
error: trailing characters

Try this: chain a second transform and watch the error skip both steps.

Facts verified 2026-11-04. The code on this page was compiled and run on the box with GCC 16.2.0 at -std=c++23 -Wall -Wextra; the outputs quoted are the observed outputs. MSVC was not tested.

Back to the daily tips index

Keep Exploring

Keep exploring the docs, samples, and free tools to continue your evaluation.