Problems with C++ exceptions
Exception models across languages
- Several comments contrast C++ with Java, Rust, Swift, Go, Python, and Elixir.
- Swift’s
throwsis praised as “error as return path”:func f() throws -> Tis conceptuallyT | Error, with mandatory handling and explicittryat call sites. - Swift 6’s typed throws are noted but seen as limited and sometimes impractical; some consider them not worth the complexity.
- Java’s checked exceptions are criticized: they interact poorly with interfaces, generics, and FP/lambdas, and lead to “exception tunneling” wrappers.
- Rust’s
Result<T,E>and panics are discussed;Resultis liked, but panics/unwinding and OOM handling are contentious. - Python’s exception behavior is viewed as similar to C++ (unchecked, can catch base type); discomfort mainly comes from C programmers who want exhaustiveness.
- Elixir-style
{:ok, v} | {:error, e}+ supervision trees is mentioned as an alternative to pervasive exceptions.
Typed vs untyped / checked vs unchecked
- Some argue exceptions should be part of a function’s contract (like
Result<T,E>or Java checked exceptions). - Others argue typed/checked exceptions leak implementation details and make APIs brittle: changing internals (e.g., adding caching) can require breaking changes to exception signatures.
- One view: in high-level code, you mostly either propagate or generically handle errors, so precise exception typing adds cost with little value.
- Another view: not knowing all possible failure modes feels unsafe and makes some developers uncomfortable.
C++ RAII, exceptions, and resource management
- The blog’s critique centers on a
File_handleRAII example and “RAISI” (resource acquisition is separate from initialization). - Multiple commenters claim the article misunderstands idiomatic C++: exceptions should usually be caught far from where they’re thrown, with RAII cleaning up automatically on unwind.
- Local try/catch around
fopenis seen as the wrong pattern; better patterns include:- A RAII wrapper that stores errno or an error code;
- Returning
std::expected<T, std::error_code>or usingstd::error_code/std::exceptionhierarchies; scope_guard/cleanup/defer-style helpers when a one-off wrapper is overkill.
Abstraction, contracts, and where to catch
- One camp: failure modes “pierce abstractions,” so trying to specify them all (checked/typed exceptions) breaks encapsulation and complicates evolution. Exceptions should be caught only near the source or at top-level boundaries.
- Another camp: allowing unknown exceptions and non-exhaustive handling is unsatisfying; they prefer explicit error codes or
Result-style returns for clarity and exhaustiveness.
Debugging and logging
- Lack of built-in stack traces for C++ exceptions is cited as a real pain point; it encourages broad try/catch and ad‑hoc logging.
- Others argue that logging on every layer (“log and throw”) is boilerplate and an antipattern; they prefer stack traces plus selective context logging.
- Chained/nested errors are proposed as a compromise, carrying both low‑level and high‑level context.
Complexity and evolution of C++
- Several participants note that modern C++ (post‑11) is complex enough that many programmers misuse exceptions, contributing to the kind of code the article critiques.
- There is mention of ongoing proposals for exception sets and
noexceptregions, but also skepticism that C++’s exception story can be cleanly “fixed” at this point.