std::current_exception 是否应该从类析构函数中的 catch 块返回非空值

Should std::current_exception return non-null from catch block in a class's destructor

本文关键字:catch 返回 空值 析构函数 exception current 是否 std      更新时间:2023-10-16

我和我的同事认为我们在Visual C++2012和2013中发现了一个错误,但我们不确定。以下代码中对std::current_exception的调用是否应该返回非null exception_ptr?在我们尝试过的大多数其他编译器上似乎都是这样:

#include <exception>
#include <stdexcept>
#include <iostream>
class A
{
public:
~A()
{
try
{
throw std::runtime_error("oh no"); 
}
catch (std::exception &)
{
std::clog << (bool)std::current_exception() << std::endl;
}
}
};
void foo ()
{
A aa;
throw std::runtime_error("oh no");
}
int main(int argc, char **)
{
try
{
foo();
}
catch(...)
{
}
return 0;
}

当在Visual C++下运行时,我们得到"0"(false,这意味着返回的exception_ptr为null)。其他编译器,如g++,打印"1"。

cppreference这样描述std::current_exception:

如果在异常处理期间调用(通常在catch子句中),则捕获当前异常对象并创建一个std::exception_ptr,该std::xception_ptr保存对该异常对象的副本或引用(如果生成副本,则由实现定义)。

如果此函数的实现需要调用new,而调用失败,则返回的指针将包含对std::bad_alloc 实例的引用

如果此函数的实现需要复制捕获的异常对象,并且其复制构造函数抛出异常,则返回的指针将包含对抛出的异常的引用。如果抛出的异常对象的复制构造函数也抛出,则返回的指针可能包含对std::bad_exception实例的引用,以打破无休止的循环。

如果在没有处理异常的情况下调用该函数,则返回一个空的std::exception_ptr。

引发异常展开堆栈,该堆栈应在aa实例上调用A类的析构函数,其中有一个捕获异常的简单try/throw/catch代码块。

当然,它没有标准那么权威,但在我看来,g++/crang是正确的,而视觉则不是(相反,这种情况发生的频率较低:p)

正如James McNellis所证实的,这绝对是一个bug。今天我很高兴发现了它。

在您的情况下,解决方法是在处理程序中调用make_exception_ptr而不是current_exception

~A()
{
try
{
throw std::runtime_error("oh no"); 
}
catch (std::exception & e)
{
std::clog << (bool)std::make_exception_ptr(e) << std::endl;
}
}

但我认为我们在catch(...)条款方面运气不佳,确实需要进行修复。

第1版:我很久以前在"connect"中报告过这个错误。它现在可以在Developper社区上找到。

第2版:该错误已在VS2019 16.3中修复。