Search code examples
c++language-lawyerstorageunionsplacement-new

Is it possible to use a union member as object storage?


I wonder whether we can use a union member as storage for an explicitly initialized and destructed object, such as in the following code:

struct X
{
    X() { std::cout << "C"; }
    ~X() { std::cout << "D"; }
};

struct X_owner
{
    union
    {
        X x;  // storage for owned object
    };

    X_owner()
    {
        new (&x) X{};
    }

    ~X_owner()
    {
        (&x)->~X();
    }
};

int main()
{
    X_owner xo;
}

The printed output is as expected only CD. Live demo: https://godbolt.org/z/M1Gov4o4d

The question is motivated by a programming assignment, where students were supposed to explicitly define storage for an object. The expected solution was a suitably aligned and sized buffer of type unsigned char or std::byte, or std::aligned_storege. However, few of them used union this way within their solutions. And I am not sure whether this is correct, though I cannot find anything wrong about it.

Note: For the sake of simplicity, I do not care about copy/move semantics in this example.


Solution

  • It is indeed valid.

    [class.base.init]

    9 In a non-delegating constructor, if a given potentially constructed subobject is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then

    • [...]
    • otherwise, if the entity is an anonymous union or a variant member ([class.union.anon]), no initialization is performed;
    • [...]

    So x is not initialized, and so is not within its lifetime until one explicitly does something with that lump of storage to create an object there. The placement new isn't even replacing an object in that storage, there was non there until the new expression. So we aren't even delving into places where std::launder may be required.

    Seems like an acceptable approach for doing it.