After stumbling on this one in a recent telephone interview, I thought I’d refresh my memory.
The following from Anthony Williams’s ACCU Overload Journal article last August provides a succinct summary of the topic.
The Abrahams Exception Safety Guarantee
These guarantees were first documented by Dave Abrahams when the C++ Standards committee were working on the 1998 C++ Standard. The idea is that code should provide one of the three guarantees – if it doesn’t, then an exception occuring in your code will result in leaked resources or corrupt data structures or both. The guarantees are:
The no-fail (or no-throw) guarantee
This is the strongest of all guarantees. A function that provides this guarantee will not throw any exceptions, and will not fail. All destructors should provide this guarantee, as should important operations like swap which provide the building blocks for the code that uses them to provide suitable exception safety guarantees.
The strong guarantee
A function that provides this guarantee is all or nothing: if it fails, then any effects are rolled back so the state of the data structure is the same as it was on entry. This requires that the function doesn’t do anything irreversible (like perform I/O), and that there are suitable operations that provide the no-fail guarantee which can be used to commit or roll back the changes.
The basic guarantee
This is the basic level you should strive for in all code: if a function fails, then it must leave the data structures in a valid state, even if that state differs from the original. For example, failure to insert a new item into a container must leave the container in a valid state, even if all the existing items have been deleted.
Any code that doesn’t provide even the basic guarantee is not exception safe.
Exceptions Make for Elegant Code, ACCU Overload Journal #86, August 2008
Writing exception safe code is hard. If you have any doubts about that statement consider the following challenge from Herb Sutter: Guru of the Week 8. In Exceptional C++, Sutter expands on his earlier post and provides the following guidelines –
Observe the canonical exception-safety rules: (1) Never allow an exception to escape from a destructor or from an overloaded operator delete() or operator delete[](); write every destructor and deallocation function as though it had an exception specification of “throw()“. (2) Always use the “resource acquisition is initialization” idiom to isolate resource ownership and management. (3) In each function, take all the code that might emit an exception and do all that work safely off to the side. Only then, when you know that the real work has succeeded, should you modify the program state (and clean up) using only nonthrowing operations.
The standard for the C++ Standard Library contains the guarantee that no destructor operation defined in the library itself will throw an exception. The C++ Standard, however, does not enforce this requirement for all C++ code so your compiler will not prevent you from creating a class that breaks the golden rule: never throw exceptions from a destructor.
Anyone looking for more information should consult the links above and also look at Abrahams’s essay: Exception-Safety in Generic Components: Lessons Learned from Specifying Exception-Safety for the C++ Standard Library.