#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?
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 ]
Related
Reading in the cppreference for lifetime, there is the following statement for storage reuse
A program is not required to call the destructor of an object to end
its lifetime if the object is trivially-destructible or if the program
does not rely on the side effects of the destructor. However, if a
program ends the lifetime of an non-trivially destructible object that
is a variable explicitly, it must ensure that a new object of the same
type is constructed in-place (e.g. via placement new) before the
destructor may be called implicitly, i.e. due to scope exit or
exception for automatic objects, due to thread exit for thread-local
objects, or due to program exit for static objects; otherwise the
behavior is undefined.
There is the following example
class T {}; // trivial
struct B
{
~B() {} // non-trivial
};
void x()
{
long long n; // automatic, trivial
new (&n) double(3.14); // reuse with a different type okay
} // okay
void h()
{
B b; // automatic non-trivially destructible
b.~B(); // end lifetime (not required, since no side-effects)
new (&b) T; // wrong type: okay until the destructor is called
} // destructor is called: undefined behavior
Why when the destructor of T is called is undefined behavior?
In the above example, if std::launder is used , is the UB issue solved? Look at the following demo
From the C++17 standard (draft here), [expr.new]:
If the new-expression creates an object or an array of objects of class type, access and ambiguity control are done for the allocation function, the deallocation function, and the constructor. If the new-expression creates an array of objects of class type, the destructor is potentially invoked.
Why would new[] invoke a destructor? It's new, after all. It isn't delete.
If construction of any object in the buffer throws an exception, the previously constructed objects must be destructed. That requires an available destructor.
You have not considered the word "potentially" in the quote you have mentioned from the standard.
It means that there is a possibility that invocation of the destructor can happen. And it will happen if construction of any object in the array throws an exception.
Combined with the following quote from [class.dtor]/12.4 which mentions [expr.new], this becomes clear.
In each case, the context of the invocation is the context of the construction of the object. A destructor is also invoked implicitly through use of a delete-expression for a constructed object allocated by a new-expression; the context of the invocation is the delete-expression. [ Note: An array of class type contains several subobjects for each of which the destructor is invoked. — end note ] A destructor can also be invoked explicitly. A destructor is potentially invoked if it is invoked or as specified in [expr.new], [class.base.init], and [except.throw]. A program is ill-formed if a destructor that is potentially invoked is deleted or not accessible from the context of the invocation.
In action:
#include <iostream>
int counter;
class Destruct
{
public:
Destruct()
{
if (counter++ > 5)
throw counter;
}
~Destruct()
{
std::cout << "Dtor called\n";
}
};
int main()
{
try
{
new Destruct[10];
}
catch (...){}
}
You'll see output something like:
Dtor called
Dtor called
Dtor called
Dtor called
Dtor called
Dtor called
How should I work with exceptions throwed from constructors of local static objects? For example I have following code:
class A
{
public:
A() {throw runtime_error("Ooops");}
};
void foo()
{
static A a = A();
cout << "Continue" << endl;
}
int main(void)
{
try
{
foo();
}
catch(...)
{
}
foo(); // Prints continue
return 0;
}
As I understand, in the case of second calling foo method, object a is treated as fully constructed object, and constructor is not called. (More over, it seems like destructor of a due first exception throwing is not called)
If that's true, then that is a compiler bug.
(However, VTT claims that this code produces the expected result with Visual Studio 2015, so I recommend double-checking your results.)
Here's the standard-mandated behaviour:
[C++14: 6.7/4]: The zero-initialization (8.5) of all block-scope variables with static storage duration (3.7.1) or thread storage duration (3.7.2) is performed before any other initialization takes place. Constant initialization (3.6.2) of a block-scope entity with static storage duration, if applicable, is performed before its block is first entered. An implementation is permitted to perform early initialization of other block-scope variables with static or
thread storage duration under the same conditions that an implementation is permitted to statically initialize a variable with static or thread storage duration in namespace scope (3.6.2). Otherwise such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined. [..]
GCC 6.3.0 correctly attempts to reconstruct the A (and thus throws again).
More over, it seems like destructor of a due first exception throwing is not called
No, it won't be. You can't destroy an object that was never successfully created in the first place.
[C++14: 15.2/2]: An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (12.6.2) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object’s destructor will be invoked. If the object was allocated in a new-expression, the matching deallocation function (3.7.4.2, 5.3.4, 12.5), if any, is called to free the storage occupied by the object.
By the way, this doesn't fix the problem, but you should just write:
static A a;
The copy-initialisation from a temporary is pointless.
Each instance of static local variable also implicitly creates a global Boolean variable that will be set to true after static variable is constructed. If constructor throws than the next time method is called there will be another attempt to construct a static variable.
Normally you are responsible for lifetime of your unrestricted union members -- and typically you do it via in-place ctor/dtor calls. But, apparently, there is at least one case when compiler helps you -- in the code below, if object construction fails it's (previously constructed) union member gets automatically destroyed (at least in MSVC 2015), i.e. we never leak.
#include <string>
struct CanThrow
{
CanThrow() { throw 0; }
};
struct A
{
A() : str{} {} // note that we don't explicitly call str dtor here
~A() { str.~basic_string(); }
union { std::string str; };
CanThrow ct;
};
int main() { try{ A a; } catch(...) {} }
Disclaimer: this code compiles on my MSVC 2015
Question -- is this guaranteed by standard and where it stipulates that?
Quite the contrary: it's not supposed to happen!
[C++11: 9.5/8]: A union-like class is a union or a class that has an anonymous union as a direct member. A union-like class X has a set of variant members. If X is a union its variant members are the non-static data members;
otherwise, its variant members are the non-static data members of all anonymous unions that are members of X.
[C++11: 15.2/2]: An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (12.6.2) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object’s destructor will be invoked. If the object was allocated in a new-expression, the matching deallocation function (3.7.4.2, 5.3.4, 12.5), if any, is called to free the storage occupied by the object.
If Visual Studio is doing so, it's non-compliant; FWIW, GCC 6.3 seems to be also.
Note that the (current) C++17 wording is different and does permit what we're observing; this change appears to have been introduced by CWG issue #1866 in 2014, so it's likely that this is one of those times that the main compilers were "ahead of the game" and don't quite abide to the standard (despite -std flags).
So the answer is, no, the standard certainly doesn't guarantee it in MSVS 2015's case, though it will in future, C++17 versions of the software.
This is a spinoff from invoking the copy constructor within the constructor.
I believe that an object is fully formed and can be expected to behave as such by the end of the initialiser list (edit: I was wrong about this though!). Specifically, member functions and accessing local state from within the constructor itself will behave exactly as they would from any other member function.
This seems to be a slightly contentious point of view though, the alternative is that only once the constructor has returned normally is the object fully formed.
The following is a quick & dirty test case for this which shows all the member fields that are mentioned in the initialiser list being initialised and those that aren't getting default constructed.
#include <cstdio>
struct noise
{
noise() { printf("noise default constructed\n"); }
noise(int x) { printf("noise integer constructed %u\n", x); }
~noise() { printf("noise dtor\n"); }
};
struct invoke : public noise
{
noise init;
noise body;
invoke() : noise(3), init(4)
{
body = noise(5);
throw *this; // try to use the object before returning normally
}
~invoke() { printf("invoke dtor\n"); }
};
int main()
{
try
{
invoke i;
}
catch (...)
{
}
}
This prints, on my machine at least,
noise integer constructed 3
noise integer constructed 4
noise default constructed
noise integer constructed 5
noise dtor
noise dtor
noise dtor
noise dtor
invoke dtor
noise dtor
noise dtor
noise dtor
As always, it's difficult to distinguish works-as-specified from works-as-my-compiler-implemented! Is this actually UB?
Is an object fully constructed at the end of the initialiser list?
No it is not. The object this is fully constructed at the end of the execution of the constructor.
However, all the members are constructed by the end of the initializer list.
The difference is subtle but it is important as it relates to the execution of the destructors. Every constructed member and base class is destructed if the this object throws an exception during the execution of the constructor. The destructor of the this object will only execute once it is fully constructed.
From the cppreference:
For any object of class or aggregate types if it, or any of its subobjects, is initialized by anything other than the trivial default constructor, lifetime begins when initialization ends.
For any object of class types whose destructor is not trivial, lifetime ends when the execution of the destructor begins.
Your example is well-defined behavior, but only just so.
To be clear, the "invoke dtor" line we're seeing is from the destruction of your exception, not the top-level i object.
Each member of the class is initialized by the time the constructor body starts, though the object itself is not initialized until the constructor body completes. This is why ~invoke is not called on the top-level invoke object.
The throw *this expression copy-initializes an invoke object from *this, which is allowed. (The standard explicitly states that "Member functions [...] can be called during construction or destruction".) Your copy-initialization is the default, which just copy-initializes all the members - which have all been initialized.
Then because your constructor body is exiting via exception, all the initialized members are destructed, the exception is propagated, caught, and then disposed of, calling ~invoke, and in turn destroying those members as well.