June 25, 2011, 7:07 p.m.
posted by madinteger
These are key functions that must not fail because they are necessary for the two key operations in transactional programming: to back out work if problems are encountered during processing, and to commit work if no problems occur. If there's no way to safely back out using no-fail operations, then no-fail rollback is impossible to implement. If there's no way to safely commit state changes using a no-fail operation (notably, but not limited to, swap), then no-fail commit is impossible to implement.
Consider the following advice and requirements found in the C++ Standard:
Destructors are special, and the compiler invokes them automatically in various contexts. If you write a classlet's call it Nefariouswhose destructor might fail (usually by throwing an exception; see Item 72), you incur the following consequences:
Deallocation functions, including specifically overloaded operator delete and operator delete, fall into the same category, because they too are used during cleanup in general, and during exception handling in particular, to back out of partial work that needs to be undone.
Besides destructors and deallocation functions, common error-safety techniques rely also on swap operations never failingin this case, not because they are used to implement a guaranteed rollback, but because they are used to implement a guaranteed commit. For example, here is an idiomatic implementation of operator= for a type T that performs copy construction followed by a call to a no-fail Swap:
(See also Item 56.)
Fortunately, when releasing a resource, the scope for failure is definitely smaller. If using exceptions as the error reporting mechanism, make sure such functions handle all exceptions and other errors that their internal processing might generate. (For exceptions, simply wrap everything sensitive that your destructor does in a try/catch(…) block.) This is particularly important because a destructor might be called in a crisis situation, such as failure to allocate a system resource (e.g., memory, files, locks, ports, windows, or other system objects).
When using exceptions as your error handling mechanism, prefer documenting this behavior by declaring these functions with a commented empty exception specification of /* throw() */. (See Item 75.)