vendredi 5 juin 2015

Intel 2015 compiler bug, RAII destruction not correct, is this a bug or am I doing something wrong?

I've got a test case where I have a class with 3 subobjects (A, B and C), and the 2nd subobject B throws an exception during construction. As I understand C++, the compiler should rewind the construction of the big class and destroy the 1st object A, but not the 2nd (B) or 3rd (C) objects.

What I see is that if I use "In-class initialization" of the first object A, then instead of the first object A getting destroyed, the 3rd object C gets destroyed. Of course it is VERY BAD to destroy an object that has not been constructed! If, for example, C was a std:unique_ptr<T>, it will probably signal a segmentation violation when it tries to free a garbage pointer.

If I use old school "member initialization", then this problem doesn't happen.

I don't see this with gcc 4.8

Here's the code. The class D exposes the bug. The class E should be identically functionally, but it does not expose the bug.

#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; try { throw "dead";} catch(...) { throw;} }
    ~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;
}

Here is the output. I except to see A constructed, B partially constructed then throws, then A destroyed, but that is not what I see for the D case.

$ 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

Aucun commentaire:

Enregistrer un commentaire