jeudi 27 avril 2017

Union common initial sequence with primitive

I am trying to better understand a rather surprising discovery regarding unions and the common initial sequence rule. The common initial sequence rule says:

 Note: One special guarantee is made in order to simplify the use of unions: If a standard-layout union contains several standard-layout structs that share a common initial sequence, and if a non-static data member of an object of this standard-layout union type is active and is one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of the standard-layout struct members; see [class.mem].  — end note

So, given:

struct A {
  int a;
  double x;
};

struct B {
  int b;
};

union U {
  A a;
  B b;
};

U u;
u.a = A{};
int i = u.b.b;

This is defined behavior and i should have the value 0 (because A and B have a CIS of their first member, an int). So far, so good. The confusing thing is that if B is replaced by simply an int:

union U {
  A a;
  int b;
};

...
int i = u.b;

According to the definition of common initial sequence:

The common initial sequence of two standard-layout struct types is...

So CISs can only apply between two standard-layout structs. And in turn:

A standard-layout struct is a standard-layout class defined with the class-key struct or the class-key class.

So a primitive type very definitely does not qualify; that is it cannot have a CIS with anything, so A has no CIS with an int. Therefore the standard says that the first example is defined behavior, but the second is UB. This simply does not make any sense to me at all; the compiler intuitively is at least as restricted with a primitive type as with a class. If this is intentional, is there any rhyme or reason (perhaps alignment related) as to why this makes sense? Is it possibly a defect?

Aucun commentaire:

Enregistrer un commentaire