- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
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/
我无法在附加行中显示“真”、“假”、"is"和“否”按钮。 我在这里有一个应用程序:Application 请按照以下步骤使用应用程序: 1。当你打开应用程序时,你会看到一个绿色的加号按钮,点击 在此
我是一名优秀的程序员,十分优秀!