考虑一个联盟,其成员共享一个共同的基类:
struct Base {
int common;
};
struct DerivedA : Base {};
struct DerivedB : Base {};
union Union {
DerivedA a;
DerivedB b;
};
无论联合在运行时“包含”什么(即,最后存储的值是什么),只要它包含某些东西,那就是Base
的子类。那么有没有办法合法地使用这个想法来访问Base
字段,而不知道工会中存储的对象的实际类型?
可能是这样的:
Base* p = reinterpret_cast<Base*>(&u);
......可能不是。也许这个:
Base* p2 = static_cast<Base *>(&u.a);
如果u.b
是最后存储的值,这是否合法?
我知道有关于适用于联合的“常见初始序列”的特殊规则,但不清楚基类是否有类似的东西。
显然它不适用于多重继承,所以也许这表明它根本不起作用。
答案 0 :(得分:4)
您输入的示例实际上是有效的,但它不允许进行许多有用的更改。
对于union的非活动成员的任何部分,唯一有效的左值到右值转换是使用活动成员([class.mem] / 23)访问该成员的公共初始序列的一部分。
但是,常见的初始序列仅为两个标准布局结构([class.mem] / 20)定义,并且有很多规则可用于标准布局结构([class] / 7) 。总结:
该课程可能不具有多态性。
该类可能没有多个具有相同类型的基类。
该类可能没有引用类型的非静态成员。
该类的所有非静态成员都具有相同的访问控制。
包括继承成员在内的所有非静态成员首先在同一个类中声明。
包括继承成员在内的所有基类和非静态成员都会递归地遵守上述所有规则。
有规则说标准布局结构的第一个非静态成员与结构具有相同的地址,并且标准布局联合的所有非静态成员具有相同的地址。联盟。但是,如果这些规则的任何组合意味着两个相同类型的对象必须具有相同的地址,则包含的struct / union不是标准布局。
(关于最后一条规则的例子:
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.
)
您的DerivedA
和DerivedB
都是标准布局,因此允许它们具有共同的初始序列。实际上,这个公共序列是每个的int
个成员,所以它们实际上是完全布局兼容的(因此可能是包含这两个结构的其他一对结构的公共初始序列的一部分)。 / p>
但是,这里最棘手的事情之一是属于同一类的所有成员的规则。如果将任何非静态成员添加到DerivedA
和/或DerivedB
,即使向两者添加相同类型的成员,更改的结构也将不再是标准布局根本没有共同的初始序列。这限制了您希望在此模式中使用继承的大多数现实原因。
答案 1 :(得分:2)
只要正在使用的结构是标准布局,通过包含该基类的任何成员访问基类是合法的。
在您提供的示例中,结构是标准布局,因此您可以通过u.a
或u.b
访问基础。