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
Related
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 <new>
struct X
{
~X() { std::cout << "destroyed" << std::endl; }
int x;
};
int main(int argc, const char * const * const argv)
{
X x{1};
new (&x) X{2};
std::cout << x.x << std::endl;
return 0;
}
Output
2
destroyed
What I know is that the destructor should always be called when placement new is used. However in this sample code, the destructor is implicitly called in the end of the main so calling it again is undefined behaviour I suppose.
so now I want to know if the destructor should always be called when placement new is used or are there certain conditions under which the destructor should not be called?
It's specified explicitly in the C++ standard
[basic.life]
5 A program may end the lifetime of any object by reusing the
storage which the object occupies or by explicitly calling the
destructor for an object of a class type with a non-trivial
destructor. For an object of a class type with a non-trivial
destructor, 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 is not used to release the storage, the
destructor shall not be implicitly called and any program that depends
on the side effects produced by the destructor has undefined behavior.
The last sentence leaves a little bit of wriggle room regarding the possibility of undefined behavior1. But ultimately, the only types for which this is well-defined are those for which the destructor is truly trivial.
1 - As of writing this, anyway.
What I know is that the destructor should always be called when placement new is used.
Yes, except when the type is trivially destructible.
In this case, you must destroy the previously constructed object before placement new:
X x{1};
x.~X();
try {
new (&x) X{2};
} catch(...) {
std::abort(); // no way to recover
}
An automatic variable of non-trivially destructible type must not go out of scope in a destroyed state. If the constructor throws, the behaviour will be undefined. Reusing memory of a non-trivial object is not recommended.
It is safer to reuse memory of a trivial object:
alignas(alignof(X)) std::byte arr[sizeof(X)];
new (arr) X{2};
x.~X();
Let's consider that during the execution of the constructor of a class S, it appears that S could be constructed using another constructor. One solution could be to make a placement new at this to reuse the storage:
struct S{
unsigned int j; //no const neither reference non static members
S(unsigned int i){/*...*/}
S(int i){
if (i>=0) {
new (this) S(static_cast<unsigned int>(i));
return;}
/*...*/
}
};
int i=10;
S x{i};//is it UB?
Storage reuse is defined in [basic.life]. I don't know how to read this section when the storage is (re)used during constructor execution.
The standard is completely underspecified in this case, and I cannot find a relevant CWG issue.
In itself, your placement new is not UB. After all, you have storage without an object, so you can directly construct an object in it. As you correctly said, the lifetime of the first object hasn't started yet.
But now the problem is: What happens to the original object? Because normally, a constructor is only called on storage without an object and the end of constructor marks the start of the lifetime of the object. But now there is already another object. Is the new object destroyed? Does it have no effect?
The standard is missing a paragraph in [class.cdtor] that says what should happen if a new object is created in the storage of an object under construction and destruction.
You can even construct even weirder code:
struct X {
X *object;
int var;
X() : object(new (this) X(4)), var(5) {} // ?!?
X(int x) : var(x) {}
} x;
is it UB?
No it is not. [basic.life]/5 says:
A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. For an object of a class type with a non-trivial destructor, 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 is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.
Emphasis on the part relevant to your class which has a trivial destructor. About the specific new (this) T; form, I found no exception to this rule in [class.cdtor] nor [class.dtor].
The following program :
#include <iostream>
using namespace std;
class Test
{
public:
Test() { cout << "Constructor is executed\n"; }
~Test() { cout << "Destructor is executed\n"; }
};
int main()
{
Test(); // Explicit call to constructor
Test t; // local object
t.~Test(); // Explicit call to destructor
return 0;
}
prints the following output:
Constructor is executed
Destructor is executed
Constructor is executed
Destructor is executed
Destructor is executed
My question is even after explicitly calling destructor in main(), why does the compiler call the destructor implicitly before exiting main()?
As a side question, apart from use in delete operator is there any other use of the strategy of calling destructor explicitly?
You've introduced undefined behavior.
Per the standard:
§ 12.4 Destructors
(11) A destructor is invoked implicitly
(11.3) — for a constructed object with automatic storage duration (3.7.3) when the block in which an object is
created exits (6.7),
and
15 Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the
destructor is invoked for an object whose lifetime has ended (3.8). [ Example: if the destructor for an
automatic object is explicitly invoked, and the block is subsequently left in a manner that would ordinarily
invoke implicit destruction of the object, the behavior is undefined. —end example ]
You explicitly call the destructor or by calling t.~Test(), it is then implicitly invoked when the object leaves scope. This is undefined.
The standard provides this note as well:
14 [ Note: explicit calls of destructors are rarely needed. One use of such calls is for objects placed at specific
addresses using a placement new-expression. Such use of explicit placement and destruction of objects can
be necessary to cope with dedicated hardware resources and for writing memory management facilities.
This is not an "explicit call to constructor":
Test(); // Explicit call to constructor
It constructs a temporary object, which implicitly calls the constructor, then the temporary immediately goes out of scope, which implicitly calls the destructor. You can't explicitly call a constructor, it gets called implicitly when you construct an object.
My question is even after explicitly calling destructor in main(), why does the compiler call the destructor implicitly before exiting main()?
Because the compiler always destroys local variables. Just because you did something dumb (manually destroyed an object that gets destroyed automatically) doesn't change that.
As a side question, apart from use in delete operator is there any other use of the strategy of calling destructor explicitly?
It's used when managing the lifetime of objects in raw memory, which is done by containers like std::vector and other utilities like std::optional.
My question is even after explicitly calling destructor in main(), why does the compiler call the destructor implicitly before exiting main()?
The destructor will be called when the object gets out of scope, regardless of whether you call the destructor explicitly or not. Don't explicitly call the destructor for objects with automatic storage duration.
As a side question, apart from use in delete operator is there any other use of the strategy of calling destructor explicitly?
Yes. When you initialize an object using the placement new expression, you need to call the destructor explicitly. Sample code from the above site:
char* ptr = new char[sizeof(T)]; // allocate memory
T* tptr = new(ptr) T; // construct in allocated storage ("place")
tptr->~T(); // destruct
delete[] ptr; // deallocate memory
the scope of t is the main function. Its created on the stack and will be destroyed at the end of the function.
That's how its supposed to work and when you call the destructor on it early, you don't change that.
You don't need to call the destructor and in this case doing so leads to it being called twice.
if you'd used
Test* t = new Test();
the destructor would not have been automatically called at the end of main.
The compiler does not care if you called the destructor explicitly or not. The local object t gets out of scope, that's why it gets destroyed and the destructor is called.
It is no good practice to call destructors explicitly. Instead you should write a method like cleanup() that can be called explicitly as well as from within the destructor. If you want to avoid that cleanup can be called twice, add something like this to you class:
private:
bool cleanupPerformed;
void cleanup()
{
if( !cleanupPerformed )
{
// ... do cleanup work here ...
cleanupPerformed = true;
}
}
For example:
struct B { int b_; };
struct D : B
{
~D()
{ // D object's lifetime ends here
d_ = 0; // (1) undefined behavior?
b_ = 0; // (2) undefined behavior also?
}
int d_;
};
The C++ Standard defines that for an object of type D, its lifetime ends when the destructor ~D() call starts.
Can we interpret this to mean that modifying the object inside the destructor, as in (1), results in undefined behavior?
If so, does the same apply if we modify the base class subobject of D, as in (2)?
Neither access is undefined, they're both perfectly okay.
While you're right that the lifetime ends when the destructor starts, you can still use the object in limited ways, defined as:
N4140 § 3.8 [basic.life] / 6
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. For an object under construction or destruction, see
[class.cdtor].
and [class.cdtor]:
N4140 § 12.7 [class.cdtor] /1
For an object with a non-trivial destructor, referring to any
non-static member or base class of the object after the destructor
finishes execution results in undefined behavior.
The above clearly states that only after the destructor finishes you can't touch members of that object.
None of the examples you've shown are undefined behavior. They are perfectly defined.
The class instance exists until the destructor returns. The object's members get destroyed not before the destructor gets called, but after it returns. As such, modifying the members of the class, in the destructor, is completely kosher. And the superclass does not get destroyed until the subclass gets completely destroyed, so modifying the superclass's members is perfectly fine, too.
Very generally speaking, an object gets destroyed by the following process:
The destructor gets called.
The class members get destroyed, in reverse order of their initial construction.
Steps 1 and 2 are repeated for the object's superclasses.
(I am ignoring virtual inheritance, for simplicity, and it's not relevant here).