The goal of this post is to show one — fairly small — backwards incompatibility in C++11. It shows how noexcept exception specifications are implicitly generated for your destructors. In short, the following program used to run successfully in C++03 (under some definition of “success”):

struct S { ~S() { throw runtime_error(""); } // bad, but acceptable }; int main() { try { S s; } catch (...) { cerr << "exception occurred"; } cout << "success"; }

In C++11, the same program will trigger the call to std::terminate .

Who does deliberately throw from destructors anyway?

You might think that this is not really an observable problem, because destructors are not supposed to throw anyway. Indeed we all have been taught that destructors had better not throw. Nonetheless, the language still allows this; and while generally dangerous, people are still allowed to do so, and explore whether there are cases where throwing destructors are useful.

Apart from resource disposal some programmers tend to use destructors (quoting Mortoray) for completion of business logic process. For instance, they use destructor to commit DB transactions:

void storeCount(int count) { transaction tr(DB); tr.execute("insert into counts values(%1)", count); } // implicit commit

One fairly popular library that uses this technique is SOCI. Even though throwing destructors are generally considered a bad and dangerous practice, this library have grown popular for years. I guess, it works on the assumption that noöne attempts to store things in DB during stack unwinding. For another inventive use of throwing destructors see this post by Stefanus Du Toit.

In this post I do not intend to argue whether it is a bad practice or not to throw from destructors, but focus on what happens when you do. But I really do not encourage you to throw from destructors.

Implicit exception specification

As one might expect, for nearly any function (except destructors and deallocation functions), unless you specify an exception specification explicitly, the function is allowed to emit any exception. The case is different for destructors: the compiler will deduce the exception specification, and it may deduce it based on different rules than you expect.

Even if you explicitly declare your destructor without any exception specification, the compiler will add one for you: the same as it would add if it had to implicitly generate the destructor. If you do not like the exception specification generated by the compiler, you have to explicitly specify it.

The implicitly generated destructor calls destructors of non-static member sub-objects and base-class sub-objects. The implicitly generated exception specification is based on exception specifications of the destructors being called. If some exception is allowed by the sub-object’s destructor, it will also be allowed by our destructor. If some exception is not allowed by any sub-objects’ destructor exception specification, it will not be allowed by our destructor’s exception specification either.

struct A {}; // 'implicit' destructor implicitly noexcept(true) struct B { int i; ~B() {} // 'explicit' destructor implicitly noexcept(true) }; struct R : A { B b; ~R() { // still implicitly noexcept(true) ! throw runtime_error(""); } };

Note what happens here: A ’s destructor is implicitly declared and defined: it has no sub-objects, so it does nothing: compiler adds exception specification noexcept(true) . Class B does have a sub-object of type int ; it also has a user-defined destructor. Because int ’s destructor does not throw any exception, so noexcept(true) specification is implicitly added, even though we cannot see this specification in the code.

The case of R ’s destructor is the most surprising. In the body of the inline destructor we unconditionally throw an exception; it is obvious that the destructor will throw. But the logic of the compiler is different. It tries to determine what the destructor would be if it had been implicitly defined: in that case it would only call its sub-objects’ destructors and do nothing inside the body: no throwing of exceptions. Neither of its sub-objects is declared to throw from destructors, so our destructor — if it were implicitly defined — would be noexcept(true) . So the compiler uses the exception specification from the hypothetical implicitly-generated definition even though we do provide our own, and even though we throw inside the body. The contents of the body is irrelevant when deducing the exception specification.

The only thing that can change the implicit noexcept(true) specifications are explicit noexcept(false) specifications. In case you need R::~R to throw, the simplest way to allow it is to declare it as noexcept(false) . There is also another way. You can declare either A::~A or B::~B as noexcept(false) :

struct A {}; // implicitly noexcept(true) struct B { int i; ~B() noexcept(false) {} }; struct R : A { B b; ~R() { // now implicitly noexcept(false) throw runtime_error(""); } };

Now the hypothetical implicitly defined destructor of R would need to call B::~B , which is noexcept(false) , therefore R::~R would also have to be noexcept(false) .

You can see that the specifications are ‘transitive’. This allows an intuitive composition of classes: if your sub-object has a throwing destructor, your destructor is also throwing.

Some technical details

I have simplified the above story a bit. I was only talking about noexcept exception specifications. C++11 still supports the old ‘dynamic’ exception specifications:

struct B { ~B() throw(Exc1, Exc2, system_error); };

They are deprecated, but still mandated by the Standard. Deducing exception specifications in such case means determining the union of all possible exceptions declared to be thrown from all possible destructors.

In case some of your sub-object’s destructors have noexcept specifications and some other have ‘dynamic’ exception specifications, the situation is even more complicated. And in fact, the C++ Standard does not specify precisely what happens in the following case:

struct A { ~A() throw(); // 'dynamic' no-throw }; struct B { ~B() noexcept; // 'static' no-throw }; struct R : A { B b; ~R() /* no-throw */ ; };

R::~R should certainly be deduced as non-throwing. But should it be a dynamic or a static version? C++11 leaves it unspecified, even though the difference may be observable in the program: violating dynamic exception specifications follows different rules (calling std::unexpected , throwing std::bad_exception ).

In case you wonder where exactly this is defined in the Standard, the two references are §12.4 ¶3 and §15.4 ¶14.

Note also, that another exceptional rule for implicit exception specifications is made for deallocation functions. The rules are simpler, though: unless you explicitly state otherwise, any deallocation function is implicitly declared as noexcept(true) . The reference to the Standard: § 15.4 ¶ 15.