Your example exactly as you typed it is in fact valid, but it doesn't allow for many useful changes.
The only valid lvalue-to-rvalue conversion on any part of an inactive member of a union is to access a part of that member's common initial sequence with the active member ([class.mem]/23).
But the common initial sequence is only defined for two standard-layout structs ([class.mem]/20), and there are quite a few rules for what qualifies as a standard-layout struct ([class]/7). Summarizing:
The class may not be polymorphic.
The class may not have more than one base class with the same type.
The class may not have a non-static member of reference type.
All non-static members of the class have the same access control.
All non-static members including inherited members are first declared in the same class.
All base classes and non-static members including inherited members obey all the above rules, recursively.
There are rules that say the first non-static member of a standard-layout struct has the same address as the struct, and that all non-static members of a standard-layout union have the same address of the union. But if any combination of these rules would imply that two objects of the same type must have the same address, the containing struct/union is not standard-layout.
(For an example of this last rule:
struct A {}; // Standard-layout
struct B { A a; }; // Standard-layout (and &b==&b.a)
union U { A a; B b; }; // Not standard-layout: &u.a==&u.b.a ??
struct C { U u; }; // Not standard-layout: U is not.
)
Your DerivedA
and DerivedB
are both standard-layout, so they are permitted to have a common initial sequence. In fact, that common sequence is the single int
member of each, so they are in fact fully layout-compatible (and could therefore be part of a common initial sequence of some other pair of structs containing the two).
One of the trickier things here, though, is the rule about all members belonging to the same class. If you add any non-static member to DerivedA
and/or DerivedB
, even if you add a member of the same type to both, the changed struct(s) is/are no longer standard-layout at all, so there is no common initial sequence. This limits most of the realistic reasons you would have wanted to use inheritance in this pattern.
DerivedA
andDerivedB
are layout compatible. But as soon as the derived classes have a non-static data member, they are no longer layout-compatible. But the base classes are, so ... not sure. – Levania