c++c++11exceptionnested-exceptions

Why doesn't C++ use std::nested_exception to allow throwing from destructor?


The main problem with throwing exceptions from destructor is that in the moment when destructor is called another exception may be "in flight" (std::uncaught_exception() == true) and so it is not obvious what to do in that case. "Overwriting" the old exception with the new one would be the one of the possible ways to handle this situation. But it was decided that std::terminate (or another std::terminate_handler) must be called in such cases.

C++11 introduced nested exceptions feature via std::nested_exception class. This feature could be used to solve the problem described above. The old (uncaught) exception could be just nested into the new exception (or vice versa?) and then that nested exception could be thrown. But this idea was not used. std::terminate is still called in such situation in C++11 and C++14.

So the questions. Was the idea with nested exceptions considered? Are there any problems with it? Isn't the situation going to be changed in the C++17?


Solution

  • The problem you cite happens when your destructor is being executed as part of the stack unwinding process (when your object was not created as part of stack unwinding)1, and your destructor needs to emit an exception.

    So how does that work? You have two exceptions in play. Exception X is the one that's causing the stack to unwind. Exception Y is the one that the destructor wants to throw. nested_exception can only hold one of them.

    So maybe you have exception Y contain a nested_exception (or maybe just an exception_ptr). So... how do you deal with that at the catch site?

    If you catch Y, and it happens to have some embedded X, how do you get it? Remember: exception_ptr is type-erased; aside from passing it around, the only thing you can do with it is rethrow it. So should people be doing this:

    catch(Y &e)
    {
      if(e.has_nested())
      {
        try
        {
          e.rethrow_nested();
        }
        catch(X &e2)
        {
        }
      }
    }
    

    I don't see a lot of people doing that. Especially since there would be an exceedingly large number of possible X-es.

    1: Please do not use std::uncaught_exception() == true to detect this case. It is extremely flawed.