Is it allowed to access a common base class of union members regardless of the stored type?
Asked Answered
H

2

4

Consider a union whose members share a common base class:

struct Base {
    int common;
};

struct DerivedA : Base {};
struct DerivedB : Base {};

union Union {
    DerivedA a;
    DerivedB b;
};

No matter what the union "contains" at runtime (i.e., what the last stored value was), as long as it contains something, that something is subclass of Base. Is there then any way to legally use this idea to access the Base field, without knowing the actual type of the object stored in the union?

Maybe something like:

Base* p = reinterpret_cast<Base*>(&u);

... probably not. Maybe this:

Base* p2 = static_cast<Base *>(&u.a);

Is it legal if u.b was the last stored value?

I know there are special rules about "common initial sequences" that apply for unions, but it isn't clear if there is something similar for base classes.

Clearly it won't work for multiple inheritance, so maybe that's an indication it won't work at all.

Heteropterous answered 9/10, 2017 at 2:12 Comment(1)
"A standard-layout class is a class that ... has the same access control (Clause 11) for all non-static data members ... either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members ... " As shown in the question, DerivedA and DerivedB 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
F
4

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.

Futilitarian answered 9/10, 2017 at 3:2 Comment(1)
Does "member" in this context (e.g., in All non-static members including inherited members are first declared in the same class) refer only to data members (fields) or also to member functions?Heteropterous
V
2

Accessing the base class thru any of the members that contain that base is legal, provided that the structs being used are standard layout.

In the example you've provided, the structs are standard layout, so you can access the base thru either u.a or u.b.

Vala answered 9/10, 2017 at 2:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.