6

I wonder whether (and how) it's possible to catch an exception thrown in a member destructor. Example:

#include <exception>

class A
{
public:
    ~A() {
        throw std::exception("I give up!");
    }
};

class B
{
    A _a;
public:
    ~B() {
        // How to catch exceptions from member destructors?
    }
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
Lukáš Bednařík
  • 2,578
  • 2
  • 15
  • 30
  • 1
    Related: [Throwing exceptions out of a destructor](http://stackoverflow.com/q/130117/476716) – OrangeDog Mar 22 '16 at 14:14
  • 2
    Destructors should be written in a way in which they cannot fail. If they can, it is a sign for bad design. [How to handle a destructor failure](http://www.cplusplus.com/forum/general/5489/) – J3soon Mar 22 '16 at 14:17
  • @J3soon I know this rule. I'm asking about the C++ syntax and how to do it. – Lukáš Bednařík Mar 22 '16 at 14:19
  • catch where? in the destructor or in the enclosing scope? – Richard Hodges Mar 22 '16 at 14:21
  • Yes it is possible to catch exceptions. You use `try` and `catch`. Some special rules apply if an exception is active already. – knivil Mar 22 '16 at 14:26
  • 1
    You need to make your destructor `noexcept(false)` if you want to throw an exception. – Simple Mar 22 '16 at 15:01
  • @Simple prior to reading your comment, I'd always assumed that destructors would default to `noexcept(false)` just like everything else does. [This blog post](https://akrzemi1.wordpress.com/2013/08/20/noexcept-destructors/) explains that precisely the opposite is true, and destructors must have an explicit `noexcept(false)` specification if they are expected to be able to throw. Interesting. – monkey0506 Jun 27 '18 at 10:29

2 Answers2

8

Yes, you can catch such an exception, using the function-try-block:

class B
{
    A _a;
public:
    ~B() try {
        // destructor body
    }
    catch (const std::exception& e)
    {
        // do (limited) stuff
    }
};

However, you cannot really do much with such an exception. The standard specifies that you cannot access non-static data members or base classes of the B object.

Also, you cannot silence the exception. Unlike with other functions, the exception will be re-thrown implicitly once the function-try-block handler of a destructor (or constructor) finishes execution.

All in all, destructors should really not throw exceptions.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • 1
    Can you let us know a reference for that last statement? [This answer](https://stackoverflow.com/a/63176363/3225396) argues that a `return` statement will suppress the automatic rethrow from the handler of a function-try-block on a destructor. – Keith Russell Sep 30 '20 at 20:54
  • [Found one](https://en.cppreference.com/w/cpp/language/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." – Keith Russell Sep 30 '20 at 21:44
  • Since the "return statement is allowed" is still I think ambiguous (doesn't explicitly say the statement effective at preventing the rethrow), here's [another](https://wiki.sei.cmu.edu/confluence/display/cplusplus/DCL57-CPP.+Do+not+let+exceptions+escape+from+destructors+or+deallocation+functions) I found, which says "Flowing off the end of a destructor function-try-block causes the caught exception to be implicitly rethrown, but an explicit return statement will prevent that from happening." – Keith Russell Oct 01 '20 at 13:05
3

Here is a self-explanatory example what you can do:

#include <stdexcept>
#include <iostream>

class A
{
public:
    ~A() noexcept(false) {
        throw std::runtime_error("I give up!");
    }
};

class B
{
    A _a;
public:
    ~B() noexcept(false) try {
        // dtor body
    }
    catch (std::exception const& e)
    {
        std::cout << "~B: " << e.what() << std::endl;
        // rethrown and you can't do anything about it
    }
};

int main()
{
    try
    {
        B b;
    }
    catch (std::exception const& e)
    {
        std::cout << "main: " << e.what() << std::endl;
    }
}

Demo

I believe, the right reference to the C++ standard (I use copy of n3376) is in 15.3 Handling an exception:

15 The currently handled exception is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor.

mloskot
  • 37,086
  • 11
  • 109
  • 136