Search code examples
c++placement-new

When I perform placement new on trivial object, Is it guaranteed to preserve the object/value representation?


struct A
{
    int x;
}

A t{};
t.x = 5;

new (&t) A;

// is it always safe to assume that t.x is 5?
assert(t.x == 5);

As far as I know, when a trivial object of class type is created, the compiler can omit the call of explicit or implicit default constructor because no initialization is required. (is that right?)

Then, If placement new is performed on a trivial object whose lifetime has already begun, is it guaranteed to preserve its object/value representation? (If so, I want to know where I can find the specification..)


Solution

  • Well, let's ask some compilers for their opinion. Reading an indeterminate value is UB, which means that if it occurs inside a constant expression, it must be diagnosed. We can't directly use placement new in a constant expression, but we can use std::construct_at (which has a typed interface). I also modified the class A slightly so that value-initialization does the same thing as default-initialization:

    #include <memory>
    
    struct A
    {
        int x;
        constexpr A() {}
    };
    
    constexpr int foo() {
        A t;
        t.x = 5;
        std::construct_at(&t);
        return t.x;
    }
    
    static_assert(foo() == 5);
    

    As you can see on Godbolt, Clang, ICC, and MSVC all reject the code, saying that foo() is not a constant expression. Clang and MSVC additionally indicate that they have a problem with the read of t.x, which they consider to be a read of an uninitialized value.

    P0593, while not directly related to this issue, contains an explanation that seems relevant:

    The properties ascribed to objects and references throughout this document apply for a given object or reference only during its lifetime.

    That is, reusing the storage occupied by an object in order to create a new object always destroys whatever value was held by the old object, because an object's value dies with its lifetime. Now, objects of type A are transparently replaceable by other objects of type A, so it is permitted to continue to use the name t even after its storage has been reused. That does not imply that the new t holds the value that the old t does. It only means that t is not a dangling reference to the old object.

    Going off what is said in P0593, GCC is wrong and the other compilers are right. In constant expression evaluation, this kind of code is required to be diagnosed. Otherwise, it's just UB.