Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

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.

like image 994
bjskishore123 Avatar asked Aug 19 '10 12:08

bjskishore123


People also ask

Does std::string allocate?

While std::string has the size of 24 bytes, it allows strings up to 22 bytes(!!) with no allocation.

Does std::string use heap?

So we can say std::string allocates short strings on the stack, but long ones -- on the heap.

Should we use union in C++?

Unions are moderately useful in C and largely useless in C++. It would be correct to say that in C++ they're a "remnant from C++ being based on C", but not to say they're "a remnant from the C only days" as if C++ supercedes C.

Why do we use std::string?

std::string class in C++ C++ has in its definition a way to represent a sequence of characters as an object of the class. This class is called std:: string. String class stores the characters as a sequence of bytes with the functionality of allowing access to the single-byte character.

Why can't we use a string inside a union?

It doesn't. The fundamental operation of a union is essentially a bitwise cast. Operations on values contained within unions are only safe when each type can essentially be filled with garbage. std::string can't, because that would result in memory corruption. Use boost::variant or boost::any.

Why can't we have a copy constructor in a union?

Because having a class with a non-trivial (copy/)constructor in a union doesn't make sense. Suppose we have If U was a struct, u.x and u.y would be initialized to an empty string and empty vector respectively.

How to properly initialize a union string?

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: This way you're simply not using the assignment function at all.

How to copy a string inside a union?

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.


2 Answers

Because having a class with a non-trivial (copy/)constructor in a union doesn't make sense. Suppose we have

union U {   string x;   vector<int> y; };  U u;  // <-- 

If U was a struct, u.x and u.y would be initialized to an empty string and empty vector respectively. But members of a union share the same address. So, if u.x is initialized, u.y will contain invalid data, and so is the reverse. If both of them are not initialized then they cannot be used. In any case, having these data in a union cannot be handled easily, so C++98 chooses to deny this: (§9.5/1):

An object of a class with a non-trivial constructor (12.1), a non-trivial copy constructor (12.8), a non-trivial destructor (12.4), or a non-trivial copy assignment operator (13.5.3, 12.8) cannot be a member of a union, nor can an array of such objects.

In C++0x this rule has been relaxed (§9.5/2):

At most one non-static data member of a union may have a brace-or-equal-initializer. [Note: if any non-static data member of a union has a non-trivial default constructor (12.1), copy constructor (12.8), move constructor (12.8), copy assignment operator (12.8), move assignment operator (12.8), or destructor (12.4), the corresponding member function of the union must be user-provided or it will be implicitly deleted (8.4.3) for the union. — end note ]

but it is still a not possible to create (correct) con/destructors for the union, e.g. how do you or the compiler write a copy constructor for the union above without extra information? To ensure which member of the union is active, you need a tagged union, and you need to handle the construction and destruction manually e.g.

struct TU {    int type;    union {      int i;      float f;      std::string s;    } u;     TU(const TU& tu) : type(tu.type) {      switch (tu.type) {        case TU_STRING: new(&u.s)(tu.u.s); break;        case TU_INT:    u.i = tu.u.i;      break;        case TU_FLOAT:  u.f = tu.u.f;      break;      }    }    ~TU() {      if (tu.type == TU_STRING)        u.s.~string();    }    ... }; 

But, as @DeadMG has mentioned, this is already implemented as boost::variant or boost::any.

like image 120
kennytm Avatar answered Oct 18 '22 09:10

kennytm


Think about it. How does the compiler know what type is in the union?

It doesn't. The fundamental operation of a union is essentially a bitwise cast. Operations on values contained within unions are only safe when each type can essentially be filled with garbage. std::string can't, because that would result in memory corruption. Use boost::variant or boost::any.

like image 42
Puppy Avatar answered Oct 18 '22 08:10

Puppy