第47条有一半错吗?
Is GotW #47 halfly wrong?
gotw# 47
错误的解决方案
"啊哈,"许多人——包括许多专家——说,"让我们使用uncaught_exception()来弄清楚我们是否可以抛出!"这就是问题2中的代码的来源。这是一个解决图解问题的尝试:
// The wrong solution
//
T::~T() {
if( !std::uncaught_exception() ) {
// ... code that could throw ...
} else {
// ... code that won't throw ...
}
}
这个想法是"我们将使用可以投掷的路径,只要它是安全的。"这种哲学在两个方面是错误的:首先,这段代码没有做到这一点;第二(也是更重要的),哲学本身是错误的。
错误的解决方案:为什么代码是不健全的
一个问题是,在某些情况下,上面的代码实际上不会像预期的那样工作。考虑:
// Why the wrong solution is wrong
//
U::~U() {
try {
T t;
// do work
} catch( ... ) {
// clean up
}
}
如果U对象在异常传播期间由于栈展开而被销毁,T::~T将无法使用"可能抛出的代码"路径,即使它可以安全地执行。
我认为上面的解释是完全不正确的,如果std::uncaught_exception返回true,那么总是让任何函数(包括析构函数)以另一个异常退出是不安全的。证明
如果在堆栈展开期间调用的任何函数,在异常对象初始化之后,在异常处理程序开始之前,以异常退出,则调用std::terminate。这类函数包括具有自动存储持续时间的对象的析构函数(其作用域已退出),以及调用(如果未省略)异常对象的复制构造函数来初始化按值捕获参数。
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在某些特定情况下是正确的,还是他在这部分解释中完全错误?
这是一个关于标准文本中"在堆栈展开期间调用的任何函数"的含义的问题。
我相信这样做的目的是为了防止"任何由堆栈展开机制直接调用的函数"以异常终止,即抛出另一个(新的)异常到活动堆栈展开会话中。此要求不应用于任何由原始堆栈展开会话调用的函数在内部调用的后续(嵌套)函数。
只要新的异常是在内部抛出和捕获的,不允许逃逸到活动堆栈展开会话中,它们就是允许的。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中该函数的第一条指令的地址吗
- QScrollArea:由垂直滚动条引起的水平滚动条
- 跟踪滚动条上的鼠标事件
- 标准 N3337 5.2.10 第 7 条中的C++"类型"是什么意思?
- 我不知道这条线是做什么的
- 如何在不使用滚动条的情况下使视图更改
- 如何将一半传递给顶点着色器?
- 在 emscripten 网页汇编正在运行期间更新进度条?
- 反转整数的一部分(一半)的函数
- 如何在 c++ 中确定一条指令(以字节为单位)在哪里结束,另一条指令从哪里开始?
- 通过水平滚动条更改标签
- 在 3ds Max 中更新进度条后,环境和效果 UI 不刷新
- Qt:无法直接为带有子项的小部件添加滚动条
- C ++ pcl_ros:如何使用最新的 100 条消息生成点云
- OnVScroll : 从 CSpinButtonCtrl 或垂直滚动条调用?
- 如何在 Win32 中用一条线连接 2 个文本框?
- C++ 概念 - 需要括号中的概念会导致 2 条冲突的错误消息
- 优雅和最短的方法,只保存一半的字节
- win32 滚动条在 C/C++ 程序中不起作用
- 第47条有一半错吗?