C++标准是否保证统一初始化是异常安全的?

xml*_*lmx 47 c++ gcc exception uniform-initialization c++17

#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 (...)
    {}
}
Run Code Online (Sandbox Code Playgroud)

vc++clang输出:

A
~A
Run Code Online (Sandbox Code Playgroud)

虽然gcc产出:

A
Run Code Online (Sandbox Code Playgroud)

这似乎是GCC的一个严重错误.

供参考,参见GCC bug 66139AndrzejKrzemieński撰写的"GCC中的一个严重错误".

我只是好奇:

C++标准是否保证统一初始化是异常安全的?

And*_*dyG 31

看来是这样的:

奇怪地发现在所有地方的§6.6/ 2跳跃声明[stmt.jump]中(N4618):

在从范围退出(但是已完成)时,在该范围内构造的具有自动存储持续时间(3.7.3)的对象将按其构造的相反顺序销毁.[注:对于临时学生,见12.2.-end note]从一个循环中转出一个循环,或者从具有自动存储持续时间的初始化变量返回过去涉及销毁具有自动存储持续时间的对象,这些对象在转移点但不在转移点的范围内. .(转入块中见6.7).[注意:但是,程序可以终止(例如通过调用std::exit()std::abort()(18.5)),而不会破坏具有自动存储持续时间的类对象. - 尾注]

我认为这里的重点是"(但已完成)"部分.这包括一个例外(但不包括导致a的事物std::terminate).


编辑

我认为更好的参考是§15.2/ 3构造函数和析构函数[except.ctor](强调我的):

如果除了委托构造函数之外的对象的初始化或销毁由异常终止,则为每个对象的直接子对象调用析构函数,对于完整对象,调用其初始化已完成的虚拟基类子对象(8.6)和其析构函数尚未开始执行,除非在破坏的情况下,类似联合类的变体成员不会被销毁.子对象以完成构造的相反顺序销毁.在进入构造函数或析构函数的函数try-block的处理程序(如果有的话)之前,对这种破坏进行排序 .

这将包括聚合初始化(我今天学到的可以称为非空的初始化)

...对于带有构造函数的对象,我们可以引用§12.6.2/ 12 [class.base.init](强调我的):

在非委托构造函数中,可能会调用每个可能构造的类类型子对象的析构函数(12.4).[注意:此规定确保在抛出异常时可以为完全构造的子对象调用析构函数(15.2). - 尾注]