是否允许访问联合成员的公共基类,而不管存储的类型如何
Is it allowed to access a common base class of union members regardless of the stored type?
考虑一个联合,其成员共享一个公共基类:
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
是最后存储的值,这合法吗?
我知道有一些关于"公共初始序列"的特殊规则适用于并集,但不清楚基类是否有类似的规则。
很明显,它不适用于多重继承,所以这可能表明它根本不起作用。
您键入的示例实际上是有效的,但它不允许进行许多有用的更改。
在并集的非活动成员的任何部分上,唯一有效的左值到右值转换是访问该成员与活动成员的公共初始序列的一部分([class.mem]/23)。
但是,公共初始序列只为两个标准布局结构([class.mem]/20)定义,并且有相当多的规则可以作为标准布局结构来定义([class]/7)。总结:
-
类可能不是多态的。
-
该类不能有多个具有相同类型的基类。
-
类不能具有引用类型的非静态成员。
-
类的所有非静态成员都具有相同的访问控制。
-
包括继承成员在内的所有非静态成员首先在同一类中声明。
-
所有基类和非静态成员(包括继承的成员)都递归地遵守上述所有规则。
-
有一些规则规定,标准布局结构的第一个非静态成员与该结构具有相同的地址,并且标准布局联合的所有非静态成员都具有相同的联合地址。但是,如果这些规则的任何组合都意味着同一类型的两个对象必须具有相同的地址,那么包含的结构/联合就不是标准布局。
(例如最后一条规则:
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
成员,因此它们实际上是完全布局兼容的(因此可能是包含这两个结构的其他结构对的公共初始序列的一部分)。
然而,这里更棘手的事情之一是关于所有成员属于同一个类的规则。如果向DerivedA
和/或DerivedB
添加任何非静态成员,即使向两者添加相同类型的成员,更改后的结构也不再是标准布局,因此没有通用的初始序列。这限制了您希望在此模式中使用继承的大多数现实原因。
通过包含基类的任何成员访问基类是合法的,前提是所使用的结构是标准布局。
在您提供的示例中,结构是标准布局,因此您可以通过u.a
或u.b
访问基础。
- std::具有相同基类的类的变体
- 是否可以初始化不可复制类型的成员变量(或基类)
- 在C++中,是否可以基于给定的标识符创建基类的新实例,反之亦然
- 基类中的函数名称解析
- C++初始化基类
- 如何通过派生类函数更改基类中的向量
- 如何定义一个纯抽象基类
- 如何使用基类指针引用派生类成员
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 使用基类指针创建对象时,缺少派生类析构函数
- 如何引用基类的派生类?
- 如果基类包含双指针成员,则派生类的构造函数
- 在模板基类中为继承类中的可选重写生成虚拟方法
- 为什么此派生对象无法访问基类的后递减方法?
- 公开最直接的基类模板名称
- 当基类是依赖类型时,这是一个缺陷吗
- 如何使基类的运算符对基类的可变参数数可见(请参阅下面的代码)?
- 模板基类中的静态变量
- C++ 继承:将子类传递给需要基类的函数并获取子类行为
- 是否允许访问联合成员的公共基类,而不管存储的类型如何