5

When another member of a union is accessed, the C++ standard used to be silent on what happens, but that was fixed to explain that member access to a union object was allowed for the purpose of assigning to that yet no existent object, which would magically create the object, by assignment to it or one of its members. Essentially the member access operator returns a promise of a future object and you have to use it with an assignment.

Given

union U {
  int i;
  long l;
};

We can create a i or l member subobject by assignment:

U u;
u.i = 2; // creates an int
u.l = 3; // creates a long (destroys the int)

But what happens when the union object is copied? How would the compiler "know" which member to "create"?

U u1, u2;
fill(&u1);
u2 = u1; // which u2 member is created here?

When using memcpy on a union from a buffer of bytes filled with numerical values (not filled from another union object), which member becomes "active"?

char buf[sizeof(U)] = { ... };
U u;
memcpy(&u, buf, sizeof u);
curiousguy
  • 8,038
  • 2
  • 40
  • 58
  • The compiler does not know. The programmer must write correct code! – Phil1970 Dec 08 '19 at 03:33
  • 1
    I think the standard is not explicit about this, but I suppose the intention is that copying a union also sets the same member active (if any). The last case would then probably not set any member active. In any case the compiler does not need to know this, because there is no behavior dependent on the active member that isn't UB. – walnut Dec 08 '19 at 03:40
  • @Phil1970 When is code correct? What are the exact requirements? – curiousguy Dec 08 '19 at 03:42
  • In my experience the whole `union` value is copied whether all of the bits are currently used or not. Can't find anything specific in the Standard though. – user4581301 Dec 08 '19 at 03:44
  • 2
    @user4581301 [The implicit copy assignment is defined to copy the object representation of the union](https://timsong-cpp.github.io/cppwp/n4659/special#class.copy.assign-13) and [that is defined to contain all bits](https://timsong-cpp.github.io/cppwp/n4659/basic#types-4). But I don't think that implies any object creation or setting of active members per se. – walnut Dec 08 '19 at 03:46
  • @walnut that completes [In simple assignment (=), the object referred to by the left operand is modified (`[defns.access]`) by replacing its value with the result of the right operand.](https://timsong-cpp.github.io/cppwp/expr.ass#2) Thanks for digging that up. – user4581301 Dec 08 '19 at 03:49
  • 1
    Side note for those playing along at home, if the members are NOT trivial, [things get ugly](https://timsong-cpp.github.io/cppwp/class.union#4). – user4581301 Dec 08 '19 at 03:51
  • @user4581301 The definitions in [expr] define only the behavior of the built-in operators for the most part. Since `union` is a class type its overloaded operators are described in [class.special]. – walnut Dec 08 '19 at 03:52
  • @walnut Is copying object representation the same as copying "underlying bytes"? – Language Lawyer Dec 08 '19 at 12:53
  • 1
    @LanguageLawyer I don't know. "*underlying bytes*" is only used twice without a direct definition of the term, but a reference to [intro.memory] that I would have understood as equivalent to "*object representation*", which is defined explicitly directly after the uses of the term "*underlying bytes*". I don't know what the intended difference between the two is supposed to be. – walnut Dec 08 '19 at 13:33
  • @walnut If it is the same, then, at least for trivially copyable unions, we can say which object becomes active after assignment. – Language Lawyer Dec 08 '19 at 13:35
  • @LanguageLawyer As far as I can tell the paragraphs about copying of "*underlying bytes*" only specify what happens to the value of (sub)objects that already exist, but doesn't include the creation of a subobject necessary to set the active member. But I really don't know for sure whether I am reading it correctly or not. I guess the intention for defaulted union assignment to set the active member is clear though, even if not spelled out. – walnut Dec 08 '19 at 13:48
  • @walnut I don't understand how can a union object `obj1` have **the same** value as union object `obj2` and yet doesn't have the same member active. – Language Lawyer Dec 08 '19 at 14:01
  • @LanguageLawyer I think you may be right. Again, I am not confident that my previous assertions are true, which is why I didn't write an answer. The only thing I wanted to say is that a wording explicitly mentioning the active member after copying is lacking. My second comment might have been worded too strongly in that regard. – walnut Dec 08 '19 at 14:25
  • @walnut I'm also not 100% sure that "object representation" and "underlying bytes" are the same, even for trivially copyable types. But there is feel that this is the same thing. And if so, it is clear which member is active after the assignment. – Language Lawyer Dec 08 '19 at 15:02
  • @LanguageLawyer Postulating that a member is "active" is one thing, starting its lifetime is another. – curiousguy Dec 11 '19 at 00:46
  • @curiousguy How is it possible? Active == refers to a subobject within lifetime. – Language Lawyer Dec 11 '19 at 02:37
  • @LanguageLawyer It is *not* possible, that's my point. It's inconsistent. You can pretend (based on the std text) that a member is active; but then, its lifetime must have started at a some point in the execution. There must be **a thing in the C++ program that is known to be able to create an object.** What is that thing? I don't see it. Many times ppl on SO have told me that `malloc(sizeof(int))` does not create an `int` because `malloc` isn't listed as a thing that creates objects, so presumably ppl treat that list of things seriously. (OTOH I never cared about these things before.) – curiousguy Dec 11 '19 at 02:48
  • 1
    @curiousguy Ok, I see. Hope P0593 will solve this. – Language Lawyer Dec 11 '19 at 03:33

0 Answers0