Consider this artificial example.
Two identical destructors, one catches the exception inside function style try/catch, one scope based try/catch.
#include <iostream>
struct A {
~A() noexcept try {
throw std::runtime_error("~A");
} catch (std::exception const &e) {
std::cout<<__LINE__<<" "<<e.what()<<std::endl;
}
};
struct B {
~B() noexcept {
try {
throw std::runtime_error("~B");
} catch (std::exception const &e) {
std::cout<<__LINE__<<" "<<e.what()<<std::endl;
}
}
};
int main() try {
// A a; // std::terminate is called, exception not caught at line 26. The try-catch block in main is not relevant.
B b;
return 0;
} catch (std::exception const &e) {
std::cout<<__LINE__<<" "<<e.what()<<std::endl;
}
Godbolt link: example
So class B
's destructor catches the exception and prints: 16 ~B
Were in case of class A
it calls terminates and prints:
terminate called after throwing an instance of 'std::runtime_error'
what(): ~A
7 ~A
May someone hint thy is this happening? (seems implicit rethrow at the end of function style try/catch, both on clang and GCC) Does standard somehow specifying this behaviour? Any quote/link may be very helpfull. Thanks in advace.
A function-level catch
on a constructor or destructor automatically rethrows the current exception upon exit:
https://en.cppreference.com/w/cpp/language/function-try-block
Every catch-clause in the function-try-block for a constructor must terminate by throwing an exception. If the control reaches the end of such handler, the current exception is automatically rethrown as if by
throw;
. The return statement is not allowed in any catch clause of a constructor's function-try-block.Reaching the end of a catch clause for a function-try-block on a destructor also automatically rethrows the current exception as if by
throw;
, but a return statement is allowed.
So, your noexcept
on ~A()
is lying to the compiler. If a noexcept
function exits because of an uncaught exception, terminate()
is called:
https://en.cppreference.com/w/cpp/language/noexcept_spec
Non-throwing functions are permitted to call potentially-throwing functions. Whenever an exception is thrown and the search for a handler encounters the outermost block of a non-throwing function, the function
std::terminate
is called