gpt4 book ai didi

c++ - GotW #47 只错了一半吗?

转载 作者:搜寻专家 更新时间:2023-10-31 00:56:02 27 4
gpt4 key购买 nike

GotW #47

The Wrong Solution

"Aha," many people -- including many experts -- have said, "let's use uncaught_exception() to figure out whether we can throw or not!" And that's where the code in Question 2 comes from... it's an attempt to solve the illustrated problem:

//  The wrong solution
//
T::~T() {
if( !std::uncaught_exception() ) {
// ... code that could throw ...
} else {
// ... code that won't throw ...
}
}

The idea is that "we'll use the path that could throw as long as it's safe to throw." This philosophy is wrong on two counts: first, this code doesn't do that; second (and more importantly), the philosophy itself is in error.

The Wrong Solution: Why the Code Is Unsound

One problem is that the above code won't actually work as expected in some situations. Consider:

//  Why the wrong solution is wrong
//
U::~U() {
try {
T t;
// do work
} catch( ... ) {
// clean up
}
}

If a U object is destroyed due to stack unwinding during to exception propagation, T::~T will fail to use the "code that could throw" path even though it safely could.

我认为上面的解释是完全不正确的,如果 std::uncaught_exception 返回真,那么让包括析构函数在内的任何函数以另一个异常退出是总是不安全的。 Prove

If any function that is called during stack unwinding, after initialization of the exception object and before the start of the exception handler, exits with an exception, std::terminate is called. Such functions include destructors of objects with automatic storage duration whose scopes are exited, and the copy constructor of the exception object that is called (if not elided) to initialize catch-by-value arguments.

C++ 中的相同单词(在 ~YYY() 中调用终止):

#include <exception>
#include <iostream>

int main(int argc, char* argv[])
{
struct YYY
{
~YYY()
{
std::cout << "during stack unwinding before throwing second exception " << std::uncaught_exception() << std::endl;
throw std::exception();
}
};
struct XXX
{
~XXX()
{
std::cout << "after first exception thrown but not catched " << std::uncaught_exception() << std::endl;
if (std::uncaught_exception())
{
try
{
YYY yyy;
}
catch (const std::exception&)
{
std::cout << "in second exception catch block " << std::uncaught_exception() << std::endl;
}
}
}
};
try
{
XXX xxx;
std::cout << "before throwing first exception " << std::uncaught_exception() << std::endl;
throw std::exception();
}
catch (const std::exception&)
{
std::cout << "in first exception catch block " << std::uncaught_exception() << std::endl;
}
std::cout << "after both exceptions catched " << std::uncaught_exception() << std::endl;

return 0;
}

我的问题是我是否遗漏了什么,Herb Sutter 对于某些特定案例是正确的,还是他在这段解释中完全错误?

最佳答案

问题是标准文本中“在堆栈展开期间调用的任何函数”是什么意思。

我认为其目的是防止“堆栈展开机制直接调用的任何函数”以异常终止,即将另一个(新的)异常抛入事件堆栈展开 session .此要求不适用于由原始堆栈展开 session 调用的任何函数内部进行的任何后续(嵌套)函数调用。

只要在内部抛出并捕获新的异常,不允许逃逸到事件堆栈展开 session 中,它们就被允许。 Herb 的解释与标准完全一致:只要在内部拦截和抑制,就可以在堆栈展开期间抛出新的异常。

您的示例调用 terminate() 的原因不同。您可能正在使用后 C++11 编译器进行编译。在 C++11 中,析构函数默认为 noexpect,这就是为什么您的 YYY::~YYY() 只是调用 terminate() 而不管堆栈展开是否正在进行,或任何其他外部条件(GCC 甚至会就此警告您)。

声明为

~YYY() throw(std::exception) // or `noexcept(false)`
{
...

测试代码的预期行为。不,它不调用 terminate():http://coliru.stacked-crooked.com/a/296ffb43b774409e

显然,Herb 的过时代码也存在同样的问题。

关于c++ - GotW #47 只错了一半吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40539674/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com