空基优化非常棒。但是,它具有以下限制:
如果空基类之一也是第一个非静态数据成员的类型或类型的基,则禁止空基优化,因为相同类型的两个基子对象需要在对象表示中具有不同的地址最派生的类型。
要解释此限制,请考虑以下代码。该static_assert
会失败。而将Foo
或更改为或Bar
从继承Base2
将避免错误:
#include <cstddef>
struct Base {};
struct Base2 {};
struct Foo : Base {};
struct Bar : Base {
Foo foo;
};
static_assert(offsetof(Bar,foo)==0,"Error!");
我完全了解这种行为。我不明白的是为什么存在这种特殊行为。显然添加它是有原因的,因为它是显式添加,而不是疏忽。这样做的理由是什么?
特别是为什么要要求两个基本子对象具有不同的地址?在上面,Bar
是一个类型,并且foo
是该类型的成员变量。我看不出为什么基类对Bar
类型为的基类很重要foo
,反之亦然。
确实,如果有的话,我希望它&foo
与Bar
包含它的实例的地址相同,因为在其他情况下它是必需的(1)。毕竟,我对virtual
继承没有任何幻想,无论如何基类都是空的,并且带有的编译Base2
显示在这种特殊情况下没有任何中断。
但是显然,这种推理在某种程度上是不正确的,并且在其他情况下,也需要这种限制。
假设答案应该针对C ++ 11或更高版本(我目前正在使用C ++ 17)。
(1)注意:EBO在C ++ 11中进行了升级,并且特别成为StandardLayoutType
s的必需项(尽管Bar
,上面不是StandardLayoutType
)。
Base *a = new Bar(); Base *b = a->foo;
使用a==b
,但是a
和b
显然是不同的对象(也许具有不同的虚拟方法覆盖)。