Question

I start playing with unions and run into trouble by compiling a simple example from a c++11 FAQ. The results are discussed here: c++11 unrestricted unions example

No I play around with some more code:

Example Code 1:

class A
{   
    private: int a;
    public:
            A(): a(0) { cout << "Create A no args" << endl; }
};  

class B
{   
    private: float b;
    public:
};

class OneOfN
{   
    public:
        union U
        {   
            A a;
            B b;           
        } u;
}; 

OneOfN n{};   // Compiles! That I did not expect, but the constructor will not be
              // called! But why?

To make the things a bit more strange here comes example 2 with two union members where no one has a default constructor! And the union itself has also no constructor provided but it compiles and works as expected. Wow!

Exmaple 2:

class A
{   
    private: int a;
    public:
        A(int _a): a(_a) { cout << "Create A with args" << endl; }

};  

class B
{   
    private: float b;
};  

class OneOfN
{   
    public:
        union U
        {   
            A a;
            B b;
        } u;
};  

int main()
{   
    OneOfN n2{1}; // This compiles as expected, constructor A(int) is called! Fine!
                  // But we see later, this is not the truth!
};

Question: If the code of example 2 is valid, why there is there no chance to use the given default constructor for a member of a union? Using the constructor with parms looks ok and works as expected. Strange to me!

Continue this mystery and addapt the example to have one more data member in class A we run into trouble. See example 3:

class A
{   
    private:
        int a;
        int aa; 
    public:
        A( int _a, int _aa): a(_a), aa(_aa) { cout << "Create with 2 args" << endl; }
};  

class B
{   
    private: float b;
    public:
};  

class OneOfN
{   
    public:
        union U
        {   
            A a;
            B b;
        } u;
};  

int main()
{   
    OneOfN n3{2,3};   // Ups! Fails 
};

Ups! Now we see that the creation of an instance of OneOfN fails. What happens? As written in example2, it is not the the constructor A(int) which is called! It is a two step init! First create a A with call to A(int) and then give that object to the copy constructor!

As a result of this, the following change to example 3 makes the example working:

    OneOfN n4{A{2,3}};  // Works! Ah! We use implicit the copy construction!

Question: 1) Is this code valid?

2) Why the copy constructor works but default constructors did not? For me as user it looks really strange.

3) Is it true that always, if no default constructor to the union is manually written, the argument to the constructor for the union will passed to the copy constructor of the FIRST union element?

My intention to give this three step example is that you can catch my ideas. As a result I hope this example give a hint for the correct usage of 'unrestricted unions' for other beginners :-) For me the behavior looks very special.

Was it helpful?

Solution

A partial, general answer: things are the way they are because many choices regarding the design of the language were based on engineering trade-offs, rather than on what is theoretically possible.

"These restrictions prevent many subtle errors and simplify the implementation of unions. The latter is important because the use of unions is often an optimization and we won’t want ‘‘hidden costs’’ imposed to compromise that. The rule that deletes constructors (etc.) from a union with a member that has a constructor (etc.) keeps simple unions simple and forces the programmer to provide complicated operations if they are needed." (The C++ Programming Language, p. 215)

Also,

"When needed, a user can define a class containing a union that properly handles union members with constructors, destructors, and assignments (§8.3.2). If desired, such a class can also prevent the error of writing one member and then reading another. It is possible to specify an in-class initializer for at most one member. If so, this initializer will be used for default initialization."

And Section 8.3.2 of the aforementioned reference starts with

"To see how we can write a class that overcomes the problems with misuse of a union [...]"

so it might be worth reading.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top