Using unions with arrays of non-trivial types

64 views Asked by At

If I have a union that contains an array of a non-trivial type (for example std::string)

using namespace std;

struct MyUnion {
    union {
        char c;
        string s[5];
    };
    MyUnion() {}
    ~MyUnion() {}
};

Is it legal (according to the C++ standard) to only initialize a part of this array and use it?

MyUnion mu;

// construct 2 strings using placement new
new (&mu.s[0]) string();
new (&mu.s[1]) string();

// do something with the strings
do_something(mu.s[0]);
do_something(mu.s[1]);

// destruct 2 strings
mu.s[0].~string();
mu.s[1].~string();

For some reason this question has been marked as a duplicate but this question is specifically about arrays and only initializing parts of the array. The proposed duplicates do not answer this question.

1

There are 1 answers

3
Passer By On

Is it legal (according to the C++ standard) to only initialize a part of this array and use it?

Yes, but with some hassle.

MyUnion mu;
new (&mu.s[1]) string(); // #1

Pointer arithmetic is only legal on array objects, and there is no array object at #1. Consequently, mu.s[1] is by itself undefined behaviour.

Prior to C++20, there's no way to create the array mu.s without also creating all string subobjects (and calling their constructors).

With C++20, we can do

MyUnion mu;
new (&mu.s) char[sizeof(mu.s)]; // #2
new (&mu.s[1]) string();

Creating a char array is one of the operations specified to implicitly create objects. After #2, an array of string is implicitly created (without creating string subobjects), thus making mu.s[1] legal.

With C++23, the preferred solution is

MyUnion mu;
std::start_lifetime_as<decltype(MyUnion::s)>(&mu.s);
new (&mu.s[1]) string();