Destructors, deallocation, `swap`, and exception type copy/move construction must never fail
Reason
We don't know how to write reliable programs if a destructor, a swap, a memory deallocation, or attempting to copy/move-construct an exception object fails; that is, if it exits by an exception or simply doesn't perform its required action.
Example, don't
class Connection {
// ...
public:
~Connection() // Don't: very bad destructor
{
if (cannot_disconnect()) throw I_give_up{information};
// ...
}
};
Note
Many have tried to write reliable code violating this rule for examples, such as a network connection that "refuses to close". To the best of our knowledge nobody has found a general way of doing this. Occasionally, for very specific examples, you can get away with setting some state for future cleanup. For example, we might put a socket that does not want to close on a "bad socket" list, to be examined by a regular sweep of the system state. Every example we have seen of this is error-prone, specialized, and often buggy.
Note
The standard library assumes that destructors, deallocation functions (e.g., operator delete), and swap do not throw. If they do, basic standard-library invariants are broken.
Note
- Deallocation functions, including
operator delete, must benoexcept. swapfunctions must benoexcept.- Most destructors are implicitly
noexceptby default. - Also, make move operations
noexcept. - If writing a type intended to be used as an exception type, ensure its copy constructor is
noexcept. In general we cannot mechanically enforce this, because we do not know whether a type is intended to be used as an exception type. - Try not to
throwa type whose copy constructor is notnoexcept. In general we cannot mechanically enforce this, because eventhrow std::string(...)could throw but does not in practice.
Enforcement
- Catch destructors, deallocation operations, and
swaps thatthrow. - Catch such operations that are not
noexcept.
See also: discussion