Exceptions — Quick Reference
C++ exceptions cheatsheet — throw/catch/rethrow, exception hierarchy, noexcept, RAII safety, exception_ptr, and when to use vs alternatives.
throw / catch / rethrow
cpp
try {
throw std::runtime_error("something failed");
} catch (const std::invalid_argument& e) {
// specific — checked first
} catch (const std::runtime_error& e) {
std::println("runtime error: {}", e.what());
} catch (const std::exception& e) {
// base class — catches all std exceptions
} catch (...) {
// catch-all — rethrow or log only
throw; // rethrow preserves original type and stack
}Standard Exception Hierarchy
cpp
std::exception
std::logic_error
std::invalid_argument
std::domain_error
std::length_error
std::out_of_range
std::runtime_error
std::range_error
std::overflow_error
std::underflow_error
std::system_error (has error_code)
std::bad_alloc (new failed)
std::bad_cast (dynamic_cast failed)
std::bad_typeid (typeid on null)
std::bad_exceptionCustom Exceptions
cpp
class AppError : public std::runtime_error {
int code_;
public:
AppError(int code, std::string msg)
: std::runtime_error{std::move(msg)}, code_{code} {}
int code() const { return code_; }
};
throw AppError{404, "resource not found"};noexcept
cpp
void safe() noexcept; // never throws — optimizer hint
void risky() noexcept(false); // may throw (default)
// noexcept operator — query at compile time
static_assert(noexcept(std::swap(a, b)));
// Conditional noexcept — forward noexcept from inner operation
template<typename T>
void wrap(T& x) noexcept(noexcept(x.process())) {
x.process();
}
// IMPORTANT: if noexcept function throws anyway → std::terminateRAII and Exception Safety
cpp
// RAII ensures cleanup even when exceptions are thrown
class ManagedFile {
FILE* f_;
public:
explicit ManagedFile(const char* path) : f_{fopen(path, "r")} {
if (!f_) throw std::system_error(errno, std::generic_category());
}
~ManagedFile() { if (f_) fclose(f_); } // runs even on exception
};
void process() {
ManagedFile f{"data.txt"}; // opened
risky_operation(); // throws? f's destructor runs anyway
// f closed automatically
}exception_ptr — Transferring Exceptions
cpp
// Capture exception in current thread
std::exception_ptr eptr;
try {
throw std::runtime_error("oops");
} catch (...) {
eptr = std::current_exception(); // capture
}
// Rethrow in another context (e.g., from a thread)
if (eptr) {
try {
std::rethrow_exception(eptr);
} catch (const std::exception& e) {
std::println("caught from thread: {}", e.what());
}
}std::terminate and set_terminate
cpp
// Causes of std::terminate:
// - noexcept function that throws
// - exception thrown during stack unwinding (destructor throws)
// - uncaught exception reaching main()
// - std::thread exits with active exception
std::set_terminate([] {
std::println(stderr, "terminate called — generating crash report");
std::abort();
});Exceptions vs Alternatives
cpp
Use exceptions when:
✅ Error is genuinely exceptional (rare, unexpected)
✅ Error must propagate up many call frames
✅ You need RAII cleanup on error paths
Use error codes / std::expected when:
✅ Error is a common/expected outcome (parse failure, file not found)
✅ Performance-critical hot path (exceptions have cost when thrown)
✅ Calling from C code or across ABI boundary
✅ Embedded systems that disable exceptions (-fno-exceptions)Exception Safety Levels
cpp
noexcept — never throws
strong — if it throws, state is unchanged (rollback semantics)
basic — if it throws, no leak, invariants held, state may change
none — if it throws, anything can happen (avoid)