Search⌘ K
AI Features

Modern Error Handling

Explore how modern C++ features such as std::optional, std::variant, and std::expected improve error handling by making success and failure states explicit. Understand when to use each approach to write safer, clearer, and more maintainable code that communicates intent directly through types.

Exceptions are a powerful mechanism for handling exceptional and unrecoverable failures, but they are not always appropriate for routine, expected error conditions. In modern C++, we strive to design interfaces where the possibility of failure is explicitly reflected in the function’s return type.

Rather than relying solely on exceptions or ambiguous sentinel values such as -1 or nullptr, modern C++ favors vocabulary types that clearly model success, failure, or the absence of a value. In this lesson, we will explore how C++20 and C++23 features enable the creation of self-documenting, robust APIs that communicate intent and failure modes directly through their types.

The limits of exceptions and return codes

We previously learned that exceptions allow us to handle errors without cluttering our main logic. However, exceptions have drawbacks:

  • Invisibility: A function signature such as int divide(int a, int b) does not indicate whether the function may throw an exception when b is zero. This lack of visibility makes it harder for callers to reason about failure cases.

  • Performance: Throwing and catching exceptions is more expensive than returning a value, which makes exceptions unsuitable for situations where failures are expected and occur frequently.

  • Semantics: Not all failures are truly exceptional. For example, attempting to look up a user who does not exist is often a normal, expected outcome rather than a critical error.

Historically, C programs relied on sentinel or “magic” return ...