2015-06-05 4 views
13

У меня есть тестовый пример, где у меня есть класс с 3 подобъектами (A, B и C), а второй подобъект B выдает исключение во время строительства. Поскольку я понимаю C++, компилятор должен перемотать конструкцию большого класса и уничтожить 1-й объект A, но не 2-й (B) или 3-й (C) объектов.Ошибка компилятора Intel 2015, уничтожение RAII не правильно, это ошибка, или я делаю что-то неправильно?

Что я вижу в том, что если я использую «В классе инициализации» первого объекта A, то вместо первого объекта A получения разрушен, 3-й объект C разрушается. Конечно, это ОЧЕНЬ ПЛОХО уничтожить объект, который еще не был построен! Если, например, C был std:unique_ptr<T>, он, вероятно, сигнализирует о нарушении сегментации, когда он пытается освободить указатель мусора.

Если я использую инициализацию члена старой школы, то эта проблема не возникает.

Я не вижу это с GCC 4.8

Вот код. Класс D раскрывает ошибку. Класс 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; 
} 

Вот выход. Я ожидаю увидеть 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 

- обновление -

В разделе standard, который описывает то, что должно произойти, 15.2.3

Для объекта класса типа любой продолжительности хранения которого инициализации или уничтожение прекращается с помощью исключения, для каждого из полностью созданных объектов вызывается деструктор , то есть для каждого подобъекта, для которого главный конструктор (12.6.2) завершил выполнение, а деструктор имеет , еще не начатое выполнение, за исключением того, что в случае уничтожения члены варианта типа объединения не уничтожаются. Субъекты уничтожаются в порядке, обратном порядку завершения строительства . Такое разрушение секвенируется до ввода обработчика блока try-try конструктора или деструктора, если любой.

+0

Для тех, кто заинтересован, наличия 'Try/catch' обертки делает разницу в стек разматывании и несоответствия в компиляторах: см http://stackoverflow.com/questions/22137693/ if-initialization-or-destroy-is -contininated-by-the-exception-which-is-not-hand –

ответ

4

Это определенно ошибка компилятора, и вы ответили на свой вопрос правильной ссылкой со стандарта: [кроме.т е р]/3, с дополнительным акцентом:

Для объекта класса типа любой продолжительности хранения которого инициализации или уничтожение завершается по исключения, деструктор вызывается для каждого из полностью построенных подобъектов объекта , то есть для каждого подобъекта , для которого главный конструктор (12.6.2) завершил выполнение, а деструктор не имеет , но начатое выполнение, за исключением того, что в случае уничтожения члены варианта объединенного типа не уничтожено. Субобъекты уничтожаются в обратном порядке завершения их строительства. Такое уничтожение секвенировано перед вводом обработчика функции-try-блока конструктора или деструктора, , если таковые имеются.

Где:

главный конструктор является первым конструктором вызывается в строительстве объекта (то есть, не целевой конструктор для построения этого объекта).

C не был полностью построен - его главный конструктор еще не был вызван - так что его деструктор еще нельзя назвать.

+0

Я был обеспокоен тем, что сделал что-то глупое, вызывая UB, после чего все ставки отключены. –

+0

@MarkLakata Все выглядит совершенно разумно из того, что я могу сказать. – Barry

1

Корпорация Intel подтвердила, что это проблема.

Составитель я был

icpc (ICC) 15.0.2 20150121 

Вы можете следить за форум Intel для обновления о том, когда она будет решена.

https://software.intel.com/en-us/comment/1827356