5
votes

Maybe I'm missing something, but IMO the 4th bullet point in iso §12.1 p5 is wrong:

X is a union and all of its variant members are of const-qualified type (or array thereof),

simply because you can't have more than one const qualified member in a union.

From §9.1 we have:

In a union, at most one of the non-static data members can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time.

Edit:

This snippet doesn't compile in Ideone

union U
{
    const int i;
    const char c;
    float x;
    U(int j, char a) : i(j), c(a) {}
};

Edit1:

The following code compiles in Coliru and Ideone. But according to 12.1 p5 4th bullet point, it shouldn't as the default constructor should be deleted.

#include <iostream>
union T
{
    const int y;
    const int x;
    const float z;
    T(int j) : y(j) {}
    T() = default;
};

int main()
{
    T t;
}

Edit2:

I also don't understand why the Standard disallows this code (see note in 9.5 p2)

struct S
{
    int i;
    S() : i(1) {}
};

union T
{
    S s;
    char c;
};

int main()
{
    T t;
}

but allows this. What's the difference?

struct S
{
    int i;
    S() : i(1) {}
};

union T
{
    S s;
    char c;
    T() {}
};

int main()
{
    T t;
}
2

2 Answers

10
votes

§12.1/5 is perfectly valid: it's just saying that no default constructor can possibly be generated if all the members are const, because then there is no member that can be left uninitialised.

§9.1 says nothing about limiting constness. It is a passage about limitations on accessing the union member's values, which is totally irrelevant.

Only one member being "active" here means that only one can be written to and read from in arbitrary sequence. As soon as you write to another member, you may only read from that member.

Here's an example, and a live demo showing that the example's union declaration is valid:

union T
{
    int x;
    const int y;
    const int z;
};

int main()
{
    T a{4};
    a.x = 5;
    a.y = 6;
    a.z = 7;
}

// $ g++-4.8 -std=c++11 -Wall -pedantic -pthread main.cpp && ./a.out
// main.cpp: In function 'int main()':
// main.cpp:14:9: error: assignment of read-only member 'T::y'
//      a.y = 6;
//          ^
// main.cpp:15:9: error: assignment of read-only member 'T::z'
//      a.z = 7;
//          ^

Live demo

Since compilation succeeds right up to the attempted assignment of T::y.


Edit: The code you've now pasted, and the error message you're complaining about, have nothing to do with the const.

initializations for multiple members of ‘U’

That is your problem. It says so right there! It makes no sense to try to initialise both members of a union, since only one may be "active" at any given time:

[C++11: 12.6.2/8]: [..] An attempt to initialize more than one non-static data member of a union renders the program ill-formed. [..]

But this has nothing at all to do with constness.

7
votes

§9.1 says/does nothing to prevent you from having multiple const members in a union.

Although it does a poor job of stating the limitation (at least IMO) all §9.1 really says is that at any given time, only one field of a union contains a meaningful value--specifically, the field that was initialized when the union was created or the field to which a value was assigned the most recently.

Now, it's certainly true that if all the members of the union are const, you won't be able to assign to any of them, so that latter provision becomes moot. That limits what you can do with such a union after it exists, but doesn't prevent you from creating such a union to start with.

The effect of 12.1/5b4 is that if all the members of the union are const, you can't just define an instance of that union without specifying an initializer, either by explicitly defining a ctor, or else by specifying an initializer when you create the instance:

union X { 
    const int a;
    const double b;
};

X x;   // won't compile

X x(2); // no problem -- x.a == 2

union Y { 
   const int a;
   const double b;

   Y() : b(0.0) {}
};

Y y;  // no problem: uses user defined ctor

The end result is that const gives roughly the same behavior applied to something inside a union as it does to things elsewhere--just for example, you can't define an object like:

const int x;

...either. You need to specify an initializer to create it:

const int x = 20;

The code you've added really is about §9.1 though, not about 12.1/5 at all. Since only one member of the union can be active at any given time, it only makes sense to initialize one member. Attempting to initialize two members would imply that both those members could hold values, but in fact only one can hold a value at any one time.

Edit: As to 12.1/5b4 applying only when all the members are const-qualified, I think that also makes sense. For example code like this:

union Z {
    int a;
    const double b;
};

Z z;

z.a = 1;

Seems perfectly acceptable. Admittedly, since it hasn't been initialized, you can never actually use Z.b in this case, but that's more or less beside the point--there's still no particularly good reason for the compiler to prevent default construction.