未捕获异常的数量可以超过一个吗?

24

C++17中引入了int uncaught_exceptions() noexcept;,取代了bool uncaught_exception() noexcept;,这引发了一个问题:是否可能同时有多个异常?

根据en.cppreference.com的说法:

返回值

  1. 当前线程中未捕获的异常对象数量。

我试图想象一种可能会出现多个异常的情况。

经过深思熟虑,我只想到了一种方法:在同一个try块内,在局部变量的destructorthrow某些东西。

然而这是不可行的。

首先,它违反了对destructor的规定,强制它们必须是noexcept。其次,可以通过noexcept(false)来纠正,但它只会调用terminate而不是进入catch块。

所以它并没有起到作用。

最后,在catch块内抛出任何东西都是很常见的,没有什么不寻常的。因为一旦进入catch块,uncaught_exceptions()会递减并变为零。

所以我怀疑是否可能出现这样的情况,即uncaught_exceptions()返回多于1

1个回答

24

该网站提供了提示:

有时即使 std::uncaught_exception() == true,抛出异常也是安全的。例如,如果 堆栈展开 导致一个对象被销毁,那么该对象的析构函数可以运行代码来抛出异常,只要在逃逸析构函数之前某个 catch 块捕获该异常。

使用此方法:我们在一个对象的析构函数中抛出一个异常,在堆栈展开期间销毁该对象并抛出另一个异常,但是在它被捕获之前,我们查看未捕获异常的数量(可以通过另一个对象的析构函数 tracker 来完成):

#include <iostream>
#include <exception>

struct tracker {
    ~tracker() {
        std::cout << std::uncaught_exceptions() << "\n";
    }
};

struct foo {
    ~foo() {
        try {
            tracker t;
            throw 123;
        } catch(...) {
            std::cout << std::uncaught_exceptions() << "\n";
        }
    }
};


int main() {
    try {
        foo f;
        throw 42;
    } catch(...) {}

}

输出:

2
1

关键点在于第二个异常不会逃脱foo的析构函数。为了看到两者,我们需要在进入foo的析构函数的catch块之前调用uncaught_exceptions。这就是我使用tracker的原因。一旦进入catch块,它再次返回1
如果没有这种双层堆栈展开,我不知道如何观察uncaught_exception == 2。也许这也是一开始被忽视并且只在c++17中修复的原因之一。另一方面,可以添加更多层以使uncaught_exception > 2

1
不知怎么的,我在维基页面上忽略了这个注释。谢谢! - unegare
13
@unegare 很好。如果你已经读过它了,我今天就学不到什么了 :P - 463035818_is_not_a_number
1
在 C++ 大部分的历史中(包括1998年首次标准化之前),都不鼓励(或假定)在堆栈展开期间由析构函数抛出异常。例如,标准容器假定元素(容器所包含的对象)的析构函数不会抛出异常[有多种理由支持这种做法,包括如果容器必须处理析构函数抛出异常时重新调整大小会对性能产生影响]。因此,从析构函数(或“双层”)抛出异常可能在 C++ 的大部分历史中并未被视为严重的使用情况。 - Peter
4
@Peter “关键点在于第二个异常不会逃离foos析构函数。” 代码中没有析构函数抛出异常。我没有从析构函数“抛出”。我是在构造函数内部抛出并在内部捕获的。据我所知,这一直都没问题。 - 463035818_is_not_a_number
2
@Peter 确切地说。一旦您有一个函数调用,在其中内部引发并捕获异常,那么它只是跟我代码中的情况相比仅仅是一个微小的步骤。您只需要在析构函数中调用此函数,析构函数在堆栈展开期间执行,然后就完成了。短暂的时刻内会有多个未捕获的异常。顺便说一下,这不一定是异常控制流的不良实践,但在抛出和捕获异常的1次调用中,抛出和捕获可能非常遥远,甚至在不同的库中。 - 463035818_is_not_a_number
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接