Strong Exception Safety in C++
Strong exception safety means commit or rollback:
If an operation succeeds, all changes become visible.
If an operation throws, observable program state remains exactly as before.
This article organizes the core guarantees and shows practical C++ patterns to get strong exception safety without overengineering.
1) The exception-safety levels
In practice, code falls into one of these levels:
No guarantee
An exception may leak resources or leave objects in an invalid state.
Basic guarantee
No resource leaks, invariants are preserved, but object value may change.
Strong guarantee
Operation is transactional: either full success or no visible effect.
No-throw guarantee (
noexcept)Operation never lets exceptions escape.
noexcept is the strongest contract, but it is usually feasible only for a subset of operations (destructors, swaps, many moves, cleanup paths).
2) The classic strong-guarantee technique: copy-and-swap
For assignment-like updates, copy-and-swap is the most common pattern.
Why it works:
Potentially-throwing work happens while constructing
rhs.After that,
swapisnoexceptand commits atomically from caller perspective.
3) std::move, throwing moves, and std::move_if_noexcept
std::move, throwing moves, and std::move_if_noexceptstd::move itself never throws; it is only a cast. What can throw is the move constructor/assignment of the target type.
This matters for containers during reallocation. To keep strong guarantees, implementations often prefer copy when move is not noexcept.
Guideline: mark move operations noexcept when you can truly guarantee it. This enables better performance and helps containers preserve strong guarantees.
4) RAII is the foundation
Strong/basic guarantees rely on reliable cleanup. RAII is the standard mechanism.
If an exception is thrown, stack unwinding destroys local objects and releases resources automatically.
5) Destructor and catch guidance
Destructors should not throw. If cleanup might throw, catch inside the destructor.
Catch exceptions only when you can recover meaningfully.
Otherwise, let exceptions propagate to a layer that can decide policy.
6) Practical checklist
Define class invariants first; guarantees are meaningless without invariants.
Prefer value types and RAII members (
std::string,std::vector, smart pointers).Use copy-and-swap for transactional updates.
Make
swapand move operationsnoexceptwhere correct.Use
std::move_if_noexceptin generic relocation/update code.
References
Effective Modern C++, Item 14: declare functions
noexceptif they will not emit exceptions.
Last updated