【发布时间】:2020-04-26 01:57:25
【问题描述】:
Empty base optimization 很棒。但是,它具有以下限制:
如果空基类中的一个也是第一个非静态数据成员的类型或类型的基类,则禁止空基类优化,因为相同类型的两个基子对象需要在其中具有不同的地址派生最多的类型的对象表示。
要解释此限制,请考虑以下代码。 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 中得到了升级,特别是对于 StandardLayoutTypes(尽管上面的 Bar 是不是StandardLayoutType)。
【问题讨论】:
-
你引用的理由(“因为相同类型的两个基本子对象需要有不同的地址”)有什么不足?相同类型的不同对象需要有不同的地址,这个要求确保我们不会违反这个规则。如果此处应用空基优化,我们可以将
Base *a = new Bar(); Base *b = a->foo;与a==b结合使用,但a和b显然是不同的对象(可能具有不同的虚拟方法覆盖)。 -
语言律师的回答是引用规范的相关部分。看来你已经知道了。
-
我不确定我是否理解您在这里寻找什么样的答案。 C++ 对象模型就是这样。存在限制是因为对象模型需要它。除此之外,您还在寻找什么?
-
@TobySpeight 相同类型的不同对象需要有不同的地址在一个行为明确的程序中很容易打破这个规则。
-
@TobySpeight 不,我不是说你忘了说生命周期:“同一类型的不同对象在它们的生命周期内” .在同一个地址可以有多个相同类型的对象,它们都是活动的。允许这样做的措辞中至少有 2 个错误。
标签: c++ class inheritance language-lawyer memory-layout