为什么 C++ 不使用 std::nested_exception 来允许从析构函数中抛出?

从析构函数抛出异常的主要问题是,在调用析构函数的那一刻,另一个异常可能正在运行"(std::uncaught_exception() == true),所以它是在这种情况下该怎么做并不明显.用新异常覆盖"旧异常将是处理这种情况的可能方法之一.但决定在这种情况下必须调用 std::terminate(或另一个 std::terminate_handler).

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 通过 std::nested_exception 类引入了嵌套异常功能.此功能可用于解决上述问题.旧的(未捕获的)异常可以嵌套到新的异常中(反之亦然?),然后可以抛出嵌套的异常.但是这个想法没有被使用.std::terminate 在 C++11 和 C++14 的这种情况下仍然被调用.

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.

所以问题.是否考虑了嵌套异常的想法?有什么问题吗?在 C++17 中情况会不会有所改变?

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?

推荐答案

当你的析构函数作为堆栈展开过程的一部分执行时(当你的对象不是作为堆栈展开的一部分创建时)1,你的析构函数需要发出一个异常.

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.

那么它是如何工作的呢?你有两个例外.异常 X 是导致堆栈展开的异常.异常 Y 是析构函数想要抛出的异常.nested_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.

所以也许你有异常Y 包含一个nested_exception(或者可能只是一个exception_ptr).那么...您如何在 catch 站点处理这个问题?

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?

如果你捕捉到Y,而它恰好有一些嵌入的X,你是怎么得到它的?记住:exception_ptr 是类型擦除;除了传递它,你唯一能做的就是重新抛出它.那么人们应该这样做:

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)
    {
    }
  }
}

我没有看到很多人这样做.特别是因为可能会有非常多的 X-es.

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

1:请不要使用 std::uncaught_exception() == true 来检测这种情况.这是非常有缺陷的.

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

相关文章