我理解C ++中的当前规则说:
如果由于异常而在已经堆栈展开的情况下抛出析构函数,则调用
std::terminate
。
在探究规则原因的原因时,我遇到了以下代码中描述的情况。
X
的析构函数抛出。
Y
在自己的析构函数中删除X
。
因此Y
的析构函数会抛出。
我不清楚Y
抛出(3.)的事实是否应该通过标准规则触发std::terminate
。我希望它不应该,并按照我的希望对gcc
进行测试。
熟悉标准法律术语的人能澄清一下吗?应该(3.)触发std::terminate
吗?
#include <iostream>
struct X {
~X() noexcept(false) {
std::cout << "Destroying X\n";
throw std::runtime_error("Exception");
}
};
struct Y {
X * x_;
explicit Y(X * x) : x_{x} { }
~Y() noexcept(false) {
std::cout << "Destroying Y\n";
delete x_;
}
};
int main() {
try {
Y y(new X());
std::cout << "Living\n";
}
catch (const std::exception & e) {
std::cout << "Caught " << e.what() << '\n';
}
}
使用g ++版本5.4.0-6ubuntu1~16.04.9和--std=c++17
我得到:
Living
Destroying Y
Destroying X
Caught Exception
答案 0 :(得分:3)
标准在[except.terminate]p1.4中说明:
- 在堆栈展开期间通过抛出异常终止对象的销毁,或
2)因为Y
超出范围而发生。 1)抛出异常,开始堆栈展开。在堆栈展开期间,更具体地说是在销毁std::terminate
时 - 这是2)。它抛出异常,因此满足该点并调用Y
。
正是您正在执行的操作,除了重要的一点:在堆栈展开期间 。当一个作用域结束时,不会发生堆栈展开,它只会在抛出异常并且超出它抛出的当前作用域时发生。
throw 1;
因异常而未被销毁。添加std::terminate
即可查看对{{1}}的投放。
因此,该条款不适用,您的代码确实有效。