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.
Related
I have this example:
struct A{
A(){std::cout << "A's def-ctor\n";}
~A(){std::cout << "A's dtor\n";}
A(A const&){std::cout << "A's copy-ctor\n";}
A& operator = (A const&){std::cout << "A's copy-assign op\n"; return *this; }
};
struct Foo{
Foo() : curMem_(INT), i_(0){}
~Foo(){
if(curMem_ == CLS_A) // If I comment out this line then what happens?
a_.~A();
}
enum {INT, CHAR, CLS_A, BOOL} curMem_;
union{
int i_;
char c_;
A a_;
bool b_;
};
};
Foo f;
f.curMem_ = Foo::CLS_A;
f.a_ = A();
f.curMem_ = Foo::BOOL;
f.b_ = true;
We know that a class default destructor doesn't know which member of a class's member of a union type is active that is why we do need to define out version of destructor. So union's member data of class type are not automatically destroyed. So What will happen if I don't explicitly call the destructor of those class type member of the union?
If I comment the line in Foo destructor or remove the destructor itself what will happen? Is it undefined behavior?
My class A doesn't manage a resource via a raw pointer then why I bother to explicitly call its destructor when an of object of it is a member of a union? Thank you!
P.S: I have this from C++ primer 5th edition Chapter 19.6 unions:
Our destructor checks whether the object being destroyed holds a string. If so, the destructor explicitly calls the string destructor (§ 19.1.2, p. 824) to free the memory used by that string. The destructor has no work to do if the union holds a member of any of the built-in types.
"The destructor has no work to do if the union holds a member of any of the built-in types." I think he could add: "or of a class type which depends on the trivial destructor". What do you think?
The exact wording of the standard given in [basic.life]p6 is:
For an object of a class type, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression ([expr.delete]) is not used to release the storage, the destructor is not implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.
(emphasis mine)
"depends on the side effects" seems pretty vague, and there are plenty of questions on stack overflow discussing this wording. Your A class's destructor seems to have the side effect of calling an I/O function, so it seems like you run into undefined behaviour.
Even if it wasn't UB, if it was a std::vector, std::string or std::fstream, you would leak resources like memory or file handles. It depends entirely on what the destructor of the class (and any members of that class) do.
Since "My class A doesn't manage a resource via a raw pointer", it should really have a trivial destructor. In which case, this point is moot and it is fine to not call the destructor.
If memory is set aside for an object (e.g., through a union) but the constructor has not yet been called, is it legal to call one of the object's non-static methods, assuming the method does not depend on the value of any member variables?
I researched a bit and found some information about "variant members" but I couldn't find info pertaining to this example.
class D {
public:
D() { printf("D constructor!\n"); }
int a = 123;
void print () const {
printf("Pointer: %p\n", &a);
};
};
class C {
public:
C() {};
union {
D memory;
};
};
int main() {
C c;
c.memory.print();
}
In this example, I'm calling print() without the constructor ever being called. The intent is to later call the constructor, but even before the constructor is called, we know where variable a will reside. Obviously the value of a is uninitialized at this point, but print() doesn't care about the value.
This seems to work as expected when compiling with gcc and clang for c++11. But I'm wondering if I'm invoking some illegal or undefined behavior here.
I believe this is undefined behavior. Your variant member C::memory has not been initialized because the constructor of C does not provide an initializer [class.base.init]/9.2. Therefore, the lifetime of c.memory has not begun at the point where you call the method print() [basic.life]/1. Based on [basic.life]/7.2:
Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways. […] The program has undefined behavior if:
[…]
the glvalue is used to call a non-static member function of the object, or
[…]
emphasis mine
Note: I am referring to the current C++ standard draft above, however, the relevant wording is basically the same for C++11 except that, in C++11, the fact that D has non-trivial initialization is crucial as what you're doing may otherwise potentially be OK in C++11…
#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 ]
I am using an inplace destructor in my code, similar to this stripped down piece of code:
#include <new>
#include <stdlib.h>
struct Node {
};
int main(int, char**) {
Node* a = reinterpret_cast<Node*>(malloc(sizeof(Node)));
new(a) Node;
Node* b = a;
b->~Node();
free(a);
}
Unfortunately this gives me a warning in Visual Studio 2015, both in Debug and Release:
warning C4189: 'b': local variable is initialized but not referenced
It compiles fine though in g++, even with -Wall. Any idea why I get the warning? Might this be a bug in the compiler? b is clearly used in the b->~Node() call.
It also seems to compile fine when I change the Node implementation to this:
struct Node {
~Node() {
}
};
But as far as I can say this should not make a difference.
There are no standards for compiler warning in C++. Hence each compiler can warn you wherever he wants, it is a matter of choice.
In your case the warning does make sense, since the default destructor may not consider as a reference (for example: all local variable are defaultly destroyed at the end of their scope).
Trivial destructor
The destructor for class T is trivial if all of the following is true:
The destructor is not user-provided (meaning, it is either implicitly declared, or explicitly defined as defaulted on its first declaration)
The destructor is not virtual (that is, the base class destructor is not virtual)
All direct base classes have trivial destructors
All non-static data members of class type (or array of class type) have trivial destructors.
A trivial destructor is a destructor that performs no action. Objects with trivial destructors don't require a delete-expression and may be disposed of by simply deallocating their storage.
Is the constructor Year() safe in this case ?
struct Year {
int year;
Year(int y) : year(y) {}
Year() { *this = Year(1970); } // *this = this->operator=(Year(1970));
};
Year y;
I think yes, because year has already been initialized with int() once the execution flows reaches the constructor body. Are there other problems to consider?
Don't consider other cases in which the same trick might cause troubles.
Sure, this will work, and is valid.
Explanation
All data members and bases have already been constructed by the time your ctor-body runs, and:
[n3290: 12.7/4] Member functions, including virtual functions
(10.3), can be called during construction or destruction (12.6.2). [..]
Not to be confused with:
[n3290: 12.7/1] For an object with a non-trivial constructor, referring to any
non-static member or base class of the object before the constructor
begins execution results in undefined behavior.
(NB. "before the constructor begins"; this clause does not apply here.)
And there's nothing in 12.8 "Copying and moving class objects" to prohibit assignment during construction.
Caveat
Note that this does not mean that the object has begun its "lifetime":
[n3290: 3.8/1]: The lifetime of an object of type T begins when:
storage with the proper alignment and size for type T is obtained, and
if the object has non-trivial initialization, its initialization is complete.
And the final step in "initialization" for a non-delegating ctor:
[n3290: 12.6.2/10]: [..] Finally, the compound-statement of the constructor body is executed.
Put altogether, this means that an object's "lifetime" doesn't begin until its most derived constructor body has finished executing.
In particular, passing a pointer to the object before it has started its life is not very useful, as doing almost anything through that pointer invokes Undefined Behaviour:
[n3290: 3.5/8]: Before the lifetime of an object has started but
after the storage which the object will occupy has been allocated
or, after the lifetime of an object has ended and before the storage
which the object occupied is reused or released, any pointer that
refers to the storage location where the object will be or was located
may be used but only in limited ways. [..]
However:
[n3290: 3.8/3]: [..] [ Note: [..] Also, the behavior of an object
under construction and destruction might not be the same as the
behavior of an object whose lifetime has started and not ended. 12.6.2
and 12.7 describe the behavior of objects during the construction and
destruction phases. —end note ]
And, as we've already explored, 12.7 kindly informs us that members may be accessed during this phase of construction.
Suggestion
Your approach is hard to follow, though; I also had to look up the above passage before I could be sure that it's valid, so it's clearly not entirely intuitive.
Fortunately C++0x introduces constructor delegation, so that you can write:
struct Year {
Year() : Year(1970) {}
Year(int y) : year(y) {}
int year;
};
(Alas, GCC 4.5.1 doesn't support this, so I cannot demonstrate it to you on ideone.com. In fact, for GCC as a whole there's only a "partial patch" at time of writing.)
What you're doing works, but what wouldn't work would be if you somehow recursively called a constructor ... that would cause problems. But since the constructor is called after all non-static data-members of the object have been constructed, and the this pointer is pointing to valid memory that can be copied into, what you've done is okay--it's not standard practice, but it's okay.
This answer is probably wrong; see Tomalak's answer and the comments. I'll leave it for historic reasons, but you're probably OK calling member functions during the constructor.
Most definitely not. If you have any T*, say p, then you may only invoke member functions via p-> if p points to an object. But an object only begins its life when its constructor has finished. So while you're in the middle of the constructor, this does not point to an object! Therefore, you can most certainly not invoke any member functions like the assignment operator on this not-yet object.
[Curiosum: It is legit to say delete this; in certain situations, you just have to make sure that the destructor does not refer to this in any way. It is also OK to pass this to some other place during the constructor, as long as it is not dereferenced, much like you can pass any garbage pointer around as long as you don't use it.]