mkmostafa
mkmostafa

Reputation: 3171

should the destructor be called with placement new even on the same type

#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?

Upvotes: 4

Views: 214

Answers (2)

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.

Upvotes: 3

eerorika
eerorika

Reputation: 238321

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();

Upvotes: 2

Related Questions