When the try-block encounters an exception, the stack is unwound. If an object was created inside the try-block, the destructor is called. If the destructor throws another exception, this exception is not caught and the program is terminated.
So if you have:
struct A {
~A () noexcept(false) {
std::cout << "A::~A" << std::endl;
throw std::runtime_error("A::~A ERROR");
}
};
And then your try-catch block is something like:
try {
A a1;
A a2;
} catch (...) {}
Then when the try-block finishes, the destructor for a2
throws, the exception is caught, then the destructor for a1
throws and the program is terminated. Everything works as expected.
But if you introduce another struct that also throws in the destructor, but inherits from A
or has an instance of A
as a member, things become confusing. For example, if you have:
struct B : A {
~B () noexcept(false) {
std::cout << "B::~B" << std::endl;
throw std::runtime_error("B::~B ERROR");
}
};
Then if you do:
try {
B b;
A a;
} catch (...) {}
The expected outcome should be that A::~A
is called an the exception is caught, then B::~B
is called an the program terminates. But instead, in all compilers I tried except MSVC, the output is:
A::~A
B::~B
A::~A
terminate called after throwing an instance of std::runtime_error
what(): A::~A ERROR
As if two exceptions were caught and the third terminated the program.
The same also happens if you define B
as:
struct B {
~B () noexcept(false) {
std::cout << "B::~B" << std::endl;
throw std::runtime_error("B::~B ERROR");
}
A a;
};
I also tried some other combinations with more structs.
Don't bother putting anything in the catch-block, because the program will never even go there.
And yes, I know that ideally destructors shouldn't even throw exceptions. It's more of a curiosity after having read an article about throwing destructors.
A
is called and with g++ 8.1 both, the destructor ofA
and then the destructor ofB
get called. – Benjamin Bihlerstd::terminate()
. – Pete Becker