Why compiler doesn't allow std::string inside union?

Viewed 30852

i want to use string inside Union. if i write as below

union U
{
   int i;
   float f;
   string s;
};

Compiler gives error saying U::S has copy constructor.

I read some other post for alternate ways for solving this issue. But i want to know why compiler doesn't allow this in the first place?

EDIT: @KennyTM: In any union, if member is initialized others will have garbage values, if none is initialized all will have garbage values. I think, tagged union just provides some comfort to access valid values from Union. Your question: how do you or the compiler write a copy constructor for the union above without extra information? sizeof(string) gives 4 bytes. Based on this, compiler can compare other members sizes and allocate largest allocation(4bytes in our example). Internal string length doesn't matter because it will be stored in a seperate location. Let the string be of any length. All that Union has to know is invoking string class copy constructor with string parameter. In whichever way compiler finds that copy constructor has to be invoked in normal case, similar method as to be followed even when string is inside Union. So i am thinking compiler could do like, allocate 4 bytes. Then if any string is assigned to s, then string class will take care of allocation and copying of that string using its own allocator. So there is no chance of memory corruption as well.

Is string not existed at the time of Union developement in compiler ? So the answer is not clear to me still. Am a new joinee in this site, if anything wrong, pls excuse me.

7 Answers

You can now do it.
Of course if you initialize any other member of the union first, or simply don't initialize the string at all, then there's a problem.
Since the string class overloads the assignment operator, you can't then initialize the string with an assignment operation:

this->union_string = std::string("whatever");

Will fail because you're still using the assignment operator.

To properly initialize a union string after you've put something else in the union or not initialized it in the first place, you have to call the constructor directly on that memory:

new(&this->union_string) std::string("whatever");

This way you're simply not using the assignment function at all.

Another concern is your compiler should make you make a destructor, and if for some reason not, you should make it anyway. Since it's a union, by the end of your class's lifetime the compiler can't know whether that union memory is used by the string or something else, so your destructor should call the string's destructor if that's the case.
So if you don't do it, you'll have a memory leak since the constructor for the string is never called, and it never knows to release the memory it's using.

In new C++ standard (I tested it in C++17), you can use a complex type as a member of union.

    struct ustring
    {
        union
        {
            string s;
            wstring ws;
        };

        bool bAscii = true;
        ~ustring()
        {
            if (bAscii)
            {
                s.~string();
            }
            else
            {
                ws.~wstring();
            }
        }
    };

However, you should be very careful. Think about you construct s but destruct ws.

Related