Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the C++ standard guarantee that uniform initialization is exception-safe?

#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++ and clang output:

A ~A 

While gcc outputs:

A 

It seems a serious bug of GCC.

For reference, see GCC bug 66139 and "A serious bug in GCC" by Andrzej Krzemieński.

I just wonder:

Does the C++ standard guarantee that uniform initialization is exception-safe?

like image 687
xmllmx Avatar asked Apr 28 '17 12:04

xmllmx


1 Answers

It seems so:

Curiously found in §6.6/2 Jump Statements [stmt.jump] of all places (N4618):

On exit from a scope (however accomplished), objects with automatic storage duration (3.7.3) that have been constructed in that scope are destroyed in the reverse order of their construction. [ Note: For temporaries, see 12.2. —end note ] Transfer out of a loop, out of a block, or back past an initialized variable with automatic storage duration involves the destruction of objects with automatic storage duration that are in scope at the point transferred from but not at the point transferred to. (See 6.7 for transfers into blocks). [ Note: However, the program can be terminated (by calling std::exit() or std::abort() (18.5), for example) without destroying class objects with automatic storage duration. —end note ]

I think the emphasis here is on the "(however accomplished)" part. This includes an exception (but excludes things that cause a std::terminate).


EDIT

I think a better reference is §15.2/3 Constructors and destructors [except.ctor] (emphasis mine):

If the initialization or destruction of an object other than by delegating constructor is terminated by an exception, the destructor is invoked for each of the object’s direct subobjects and, for a complete object, virtual base class subobjects, whose initialization has completed (8.6) and whose destructor has not yet begun execution, except that in the case of destruction, the variant members of a union-like class are not destroyed. The subobjects are destroyed in the reverse order of the completion of their construction. Such destruction is sequenced before entering a handler of the function-try-block of the constructor or destructor, if any.

This would include aggregate initialization (which I learned today can be called non-vacuous initialization)

...and for objects with constructors we can cite §12.6.2/12 [class.base.init](emphasis mine):

In a non-delegating constructor, the destructor for each potentially constructed subobject of class type is potentially invoked (12.4). [ Note: This provision ensures that destructors can be called for fully-constructed subobjects in case an exception is thrown (15.2). —end note ]

like image 138
AndyG Avatar answered Oct 04 '22 16:10

AndyG