Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading