Traditional exceptions are excellent for exceptional, unexpected events (like hardware failure), but using them for expected errors (like a file not found or invalid user input) degrades performance. Modern C++ provides lightweight, monadic alternatives that avoid exceptions entirely.
Let's look at std::optional and C++23 std::expected.
Nullable States: `std::optional` (C++17)
A std::optional<T> is a stack allocated wrapper that holds either a valid value of type T or nothing (std::nullopt). It represents a function that can fail to return a value, without using exceptions or magic null values:
Value or Error: `std::expected` (C++23)
Often, returning an empty state is not enough; you want to return a detailed error code on failure. C++23 solved this by introducing std::expected<T, E>. It holds either the expected value of type T or an error object of type E:
Both std::optional and std::expected are stored on the stack and compile to simple machine branches, ensuring zero exception table overhead.
* The Mailbox Metaphor: Using optional is like checking your physical mailbox. Instead of screaming and calling the post office (throwing an exception) if there is no mail, you simply open the box and find it empty (std::nullopt). It is a normal, expected result that you check calmly.
Monadic Chaining: and_then and transform
C++23 introduces monadic operations for std::optional and std::expected. Instead of writing deeply nested if statements to check value statuses, you can chain operations cleanly using .and_then() (which maps functions returning optionals/expecteds) and .transform() (which maps functions returning raw values):