#include <iostream>
using namespace std;
struct A
{
A() { cout << "A" << endl; }
~A() { cout << "~A" << endl; }
};
A Ok() { return {}; }
A NotOk() { throw "NotOk"; }
struct B
{
A a1;
A a2;
};
void f(B) {}
int main()
{
try
{
f({ Ok(), NotOk() });
}
catch (...)
{}
}
vc++
和clang
输出:
A
~A
虽然gcc
输出:
A
这似乎是海湾合作委员会的一个严重错误。
如需参考,请参阅GCC bug 66139和"A serious bug in GCC" by Andrzej Krzemieński。
我只是想知道:
C ++标准是否保证统一初始化是异常安全的?
答案 0 :(得分:31)
似乎如此:
奇怪地在所有地方的§6.6/ 2跳跃声明[stmt.jump]中找到(N4618):
退出范围(无论多么已完成),具有自动对象 在该范围内构建的存储持续时间(3.7.3) 按其建造的相反顺序销毁。 [注意:为 临时的,见12.2。 -end note]转出一个循环,出一个 使用自动存储功能阻止或返回已初始化的变量 持续时间涉及使用自动存储破坏对象 在转移点但不在转移点的范围内的持续时间 转移到的点。 (转入块中见6.7)。 [ 注意: 但是,程序可以终止(通过调用
std::exit()
或 例如std::abort()
(18.5),不破坏类对象 具有自动存储持续时间 - 后注]
我认为这里强调的是“(但已完成)”部分。这包括一个例外(但不包括导致std::terminate
的事物)。
我认为更好的参考是§15.2/ 3构造函数和析构函数[except.ctor](强调我的):
如果初始化或销毁除了之外的对象 委托构造函数由异常终止,析构函数 为每个对象的直接子对象调用,对于a 完整对象,虚拟基类子对象,其初始化 已经完成(8.6)并且其析构函数尚未开始执行, 除了在破坏的情况下,a。的变体成员 类似联盟的阶级没有被摧毁。子对象被摧毁 完成他们的建设的相反顺序。这样 在进入处理程序之前对销毁进行排序 构造函数或析构函数的 function-try-block ,如果有的话。
这将包括聚合初始化(我今天学到的可以称为非空的初始化)
...对于带有构造函数的对象,我们可以引用§12.6.2/ 12 [class.base.init](强调我的):
在非委托构造函数中,每个潜在的析构函数 可能会调用构造的类类型的子对象(12.4)。 [ 注意:此规定可确保可以调用析构函数 完全构造的子对象,以防抛出异常(15.2)。 - 后注]