空派生优化

Empty derived optimization

本文关键字:优化 派生      更新时间:2023-10-16

大多数C++程序员都知道空基类优化是一种技术/习惯用法空的子类会发生什么例如

class EmptyBase {
int i;
};
template<typename T>
class Derived : T { 
};
std::cout << sizeof(Derived<EmptyBase>); // Is there a standard verdic on this?

类似于EBO,应该有一个EDO声明,因为派生类不提供任何更多的成员,也不向其参数化类型引入任何虚拟成员,所以它不应该需要更多的内存。考虑到可能出现类似情况的各种情况(多重继承、单一继承…):

  • 这样的优化标准/可能吗
  • 如果是,这种优化的机制是什么,它们与EBO的类似吗

注意:使用从其参数化类型派生的类模板是相当典型的。主题是关于这种情况下的空间浪费

标准本身不包含"空基类"情况。相反,它说(参见1.8):

[A]大多数派生对象应具有非零大小,并应占用一个或多个字节的存储。基类子对象的大小可能为零。

和:

除非对象是[…]大小为零的基类子对象,否则该对象的地址是它所占用的第一个字节的地址。如果一个对象是另一个对象的子对象,或者至少有一个是零大小的基类子对象并且它们具有不同的类型,则两个对象[…]可能具有相同的地址;否则,它们应具有不同的地址。

和(第9条):

类类型的完整对象和成员子对象的大小应为非零。脚注:基类子对象不受此约束。

这从来没有说只有空基地才适合任何形式的布局更改,并为"挤压"布局留下了充足的空间:例如:

struct A {}; struct B : A { int x; };             // "equivalent" to { int }
struct X { int a; }; struct Y : X {};             // "equivalent" to { int }

考虑B b;Y y;b的地址、bA-子对象(即&static_cast<A&>(b))的地址和b.x的地址可能相同,并且类似地,y的地址、yX-子对象和y.a的地址相同。

唯一不起作用的是:

struct S {}; struct T { S s; };                   // "equivalent" to { char }

我所说的"equivalent"是指最明智、节省空间的实现。

一个更有趣的案例如下:

struct Foo { int x; char a; };                    // { int, char, char[3] }
struct Bar : Foo { short q; };                    // { int, char, char, short }

本例假定sizeof(int) == 4sizeof(short) == 2。由于对齐原因,我们有sizeof(Foo) == 8,但sizeof(Bar)也是8,尽管有更多的数据成员。


标准的另一个相关部分是9.2/13:

具有相同访问控制(第11条)的(非并集)类的非静态数据成员被分配,以便稍后的成员在类对象中具有更高的地址。具有不同访问控制的非静态数据成员的分配顺序未指定(11)。实施一致性要求可能会导致两个相邻成员不能立即相互分配;管理虚拟功能(10.3)和虚拟基类(10.1)的空间需求也可能如此

最后,9.2/10说标准布局类在开始时没有填充,因此它们的地址等于"初始成员"的地址。由于标准布局要求所有基都是空的,或者派生类本身没有数据成员,这意味着标准布局类必须采用一种"空基"优化,而我上面的BY布局的初始部分实际上是强制性的。