英特尔2015编译器错误,RAII破坏不正确,这是一个错误还是我做错了什么?

Mar*_*ata 13 c++ icc c++11

我有一个测试情况下我有3子对象类(A,BC),以及第二子对象B施工期间抛出异常.据我了解C++,编译器应该回退大类的构造并销毁第一个对象A,而不是第二个(B)或第三个(C)对象.

我看到的是,如果我使用第一个对象的"类内初始化" A,那么第三个对象将被A销毁,而不是第一个对象C被破坏.当然,破坏尚未构建的对象是非常糟糕的!例如,如果C是a std:unique_ptr<T>,它可能会在尝试释放垃圾指针时发出分段违规信号.

如果我使用旧学校"成员初始化",那么这个问题就不会发生.

用gcc 4.8我没看到这个

这是代码.该类D公开了这个bug.该类E应该具有相同的功能,但它不会暴露该错误.

#include <iostream>

using namespace std;


struct A {
    A(const string& x) : x_(x) { cout << "A::A()" << (void*)this <<endl; }
    ~A() { cout << "A::~A() " << (void*)this<< endl;}
    string x_;
};

struct B {
    B(const A& a)  { cout << "B::B()" << endl; throw "dead"; }
    ~B() { cout << "B::~B()" << endl;}
};

struct C {
    C()  { cout << "C::C()" << endl; }
    ~C() { cout << "C::~C()" << endl;}
};


struct D  {
    A a{"foo"}; // "new school In-class initialization"
    B b{a};
    C c;
    D() { cout <<"D::D()" << endl; }
    ~D() { cout <<"D::~D()" << endl; }
};

struct E {
    A a;
    B b;
    C c;
    E()
        :a{"foo"}  // "old school member initialization"
        ,b(a)
        { cout <<"E::E()" << endl; }
    ~E() { cout <<"E::~E()" << endl; }
};

int main()
{
   try {
       D d;
   }
   catch(...)
   {
       cout << "got exception" << endl;
   }

   try {
       E e;
   }
   catch(...)
   {
       cout << "got exception" << endl;
   }

   return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是输出.我希望看到A构造,B部分构造然后抛出,然后A被摧毁,但这不是我看到的D情况.

$ icpc -std=c++11 test.cpp
$ ./a.out
A::A()0x7fffe0a5ee90
B::B()
C::~C()
got exception

A::A()0x7fffe0a5eea0
B::B()
A::~A() 0x7fffe0a5eea0
got exception
Run Code Online (Sandbox Code Playgroud)

- 更新 -

描述应该发生什么的标准部分是15.2.3

对于任何存储持续时间的类类型的对象,其初始化或销毁由异常终止,对每个对象的完全构造的子对象调用析构函数,即对于主构造函数(12.6.2)具有的每个子对象.完成执行并且析构函数尚未开始执行,除了在破坏的情况下,类型联合类的变体成员不会被销毁.子对象以完成构造的相反顺序销毁.在进入构造函数或析构函数的函数try-block的处理程序(如果有的话)之前,对这种破坏进行排序.

Bar*_*rry 4

这绝对是一个编译器错误,并且您已经使用标准中的正确参考回答了自己的问题:[ except.ctor] / 3,并强调:

\n\n
\n

对于任何存储持续时间的类类型的对象,其初始化或销毁因异常而终止,将为每个对象\xe2\x80\x99s完全构造的子对象调用析构函数,即为每个\n 子对象调用析构函数其中主构造函数 (12.6.2) 已完成执行,而析构函数尚未开始执行,但在销毁的情况下,类联合类的变体成员不会被销毁。子对象按照其构造完成的相反顺序被销毁。在进入构造函数或析构函数(如果有)的函数 try 块的处理程序之前,会对此类销毁进行排序。

\n
\n\n

在哪里:

\n\n
\n

构造函数是在对象构造过程中调用的第一个构造函数(即,不是该对象构造的目标构造函数)。

\n
\n\n

C还没有完全构造 - 它的主要构造函数甚至还没有被调用 - 所以它的析构函数不应该被调用。

\n