2

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.

Eduard Rostomyan
  • 7,050
  • 2
  • 37
  • 76
  • I know this is tagged `language-lawyer` but do you have a particular reason for asking this? Because throwing an exception in a destructor is a really, really bad idea. – Paul Sanders Nov 13 '22 at 20:39
  • @PaulSanders, not trying to solve any particular problem. Just was bumped to this with colleagues and trying to understand why is it the way it is, and what standard says about it, if it of course says anything.. – Eduard Rostomyan Nov 13 '22 at 20:42
  • 1
    https://timsong-cpp.github.io/cppwp/n4868/except.handle#14.sentence-1 – Language Lawyer Nov 13 '22 at 20:42
  • 3
    Dup of [Exception is caught twice](https://stackoverflow.com/questions/11574995/exception-is-caught-twice) – Language Lawyer Nov 13 '22 at 20:45

1 Answers1

2

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

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770