Modern Error Handling
Modern Error Handling
Model absence, alternatives, and recoverable failures with modern standard-library types.
Modern Error Handling
Model absence, alternatives, and recoverable failures with modern standard-library types.
Many codebases still jump straight from raw return codes to exceptions. Modern C++ gives you more expressive choices:
std::optional<T> for "maybe a value"std::variant<Ts...> for "one of several valid alternatives"std::expected<T, E> for "either a value or a typed error"Choosing the right model makes APIs easier to read and test.
std::optionalstd::optional<int> find_port(std::string_view service_name) {
if (service_name == "http") {
return 80;
}
return std::nullopt;
}
Use optional when absence is expected and there is no need to explain why the value is missing.
std::optional<std::string_view> lookup_env(std::string_view key) {
if (key == "MODE") {
return "release";
}
return std::nullopt;
}
This style is useful when "not found" is a normal outcome and the caller can choose a default.
std::variantusing Token = std::variant<int, std::string>;
variant works well when multiple result shapes are all valid domain states.
std::expectedenum class ParseError {
empty_input,
invalid_digit,
overflow,
};
std::expected<int, ParseError> parse_int(std::string_view text) {
if (text.empty()) {
return std::unexpected(ParseError::empty_input);
}
return 42;
}
Now the caller handles success and failure explicitly:
if (auto value = parse_int(input)) {
std::cout << "parsed: " << *value << '\n';
} else {
std::cout << "parse failed\n";
}
std::expected<int, ParseError> load_port(std::string_view text) {
auto parsed = parse_int(text);
if (!parsed) {
return std::unexpected(parsed.error());
}
if (*parsed < 1 || *parsed > 65535) {
return std::unexpected(ParseError::overflow);
}
return *parsed;
}
The benefit is that both success and error paths remain visible in normal code flow.
expectedexpected when local control flow should branch on success or error as a normal part of the API.[[nodiscard]].-1 on failure as std::optional<int>.std::expected<T, Error>.std::variant and visit it with std::visit.