15

Consider the following code snippet:

union
{
   int a;
   float b;
};

a = /* ... */;

b = a;               // is this UB?
b = b + something;

Is the assignment of one union member to another valid?

timrau
  • 22,578
  • 4
  • 51
  • 64
The Mask
  • 17,007
  • 37
  • 111
  • 185
  • I trimmed the post down a bit, I hope that's OK. Feel free to revert. – Kerrek SB Feb 25 '14 at 18:30
  • In C the answer would be that this is well defined since type punning is allowed [since C89](http://stackoverflow.com/questions/2310483/purpose-of-unions-in-c-and-c#comment26826326_2313676) in [C++ it is not as clear](http://stackoverflow.com/questions/2310483/purpose-of-unions-in-c-and-c). So the question here is `b = a` just considered making `b` active? Otherwise self assignment should be fine as per `1.9 p15`. – Shafik Yaghmour Feb 25 '14 at 18:36
  • Yes, you are right. Type punning seems to be ambiguous in C++. Deleted my answer. Thanks for pointing out. – shibumi Feb 25 '14 at 18:52
  • I wonder if [this answer](http://stackoverflow.com/a/10779755/596781) is relevant somehow. Also, it would be interesting if you considered a union of user-defined types. – Kerrek SB Feb 25 '14 at 19:14
  • @KerrekSB [this conversation on WG21 UB study group mailing list](http://www.open-std.org/pipermail/ub/2013-July/000083.html) seems to indicate this is very fuzzy indeed. I have to think about but I think the answer is that this is unspecified :-( – Shafik Yaghmour Feb 25 '14 at 19:22
  • @KerrekSB: It's more readable than before now. Thanks. English isn't my native language speaker. – The Mask Feb 25 '14 at 19:43
  • https://stackoverflow.com/a/65190895/1290868 – myuce Sep 22 '21 at 16:10

2 Answers2

4

Unfortunately I believe the answer to this question is that this operation on unions is under specified in C++, although self assignment is perfectly ok.

Self assignment is well defined behavior, if we look at the draft C++ standard section 1.9 Program execution paragraph 15 has the following examples:

void f(int, int);
void g(int i, int* v) {
    i = v[i++]; // the behavior is undefined
    i = 7, i++, i++; // i becomes 9

    i = i++ + 1; // the behavior is undefined
    i = i + 1; // the value of i is incremented

    f(i = -1, i = -1); // the behavior is undefined
}

and self assignment is covered in the i = i + 1 example.

The problem here is that unlike C89 forward which supports type-punning in C++ it is not clear. We only know that:

In a union, at most one of the non-static data members can be active at any time

but as this discussion in the WG21 UB study group mailing list shows this concept is not well understood, we have the following comments:

While the standard uses the term "active field", it does not define it

and points out this non-normative note:

Note: In general, one must use explicit destructor calls and placement new operators to change the active member of a union. — end note

so we have to wonder whether:

b = a;

makes b the active member or not? I don't know and I don't see a way to prove it with the any of the current versions of the draft standard.

Although in all practicality most modern compilers for example gcc supports type-punning in C++, which means that the whole concept of the active member is bypassed.

Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • 1
    If the definedness of `a = b;` is not clear, then `auto x = b; a = x;` is not clear either, in my view. But clearly the latter must be OK. The Standard says that reading the right side is sequenced before the actual assignment in an expression `a = b`. So in both cases we have a sequence, and I don't see anything otherwise more critical in `a = b` (at least among the things that I could point at in the spec. Sure it "looks more critical" to me, but subject feeling doesn't count). – Johannes Schaub - litb Feb 25 '14 at 21:40
  • @JohannesSchaub-litb are you disagreeing with the conclusion or just that I bothered to mention that self assignment is well-defined? – Shafik Yaghmour Feb 26 '14 at 05:19
  • 1
    @ShafikYaghmour I don't see any type punning here; `b = a;` requests to read the `int` value of `a`, perform a conversion to `float`, and store the result in `b`. The question is whether it is undefined due to the storage of `a` and `b` overlapping. "Type punning" is the same as aliasing, it means to try and circumvent the type system by interpreting one value's representation as that of a value of different type; but that is not happening in this case. – M.M May 06 '14 at 03:39
0

I would expect that unless the source and destination variables are the same type, such a thing would be Undefined Behavior in C, and I see no reason to expect C++ to handle it any differently. Given long long *x,*y;, some compilers might process a statement like *x = *y >>8; by generating code to read all of *y, compute the result, and store it to *x, but a compiler might perfectly legitimately write code that copied parts of *y to *x individually. The standard makes clear that if *x and *y are pointers to the same object of the same type, the compiler must ensure that no part of the value gets overwritten while that part is still needed in the computation, but compilers are not required to deal with aliasing in other situations.

supercat
  • 77,689
  • 9
  • 166
  • 211