【发布时间】:2011-06-05 04:03:39
【问题描述】:
在 C++ 中,大多数优化都源自 as-if 规则。也就是说,只要程序表现得好像没有进行优化,那么它们就是有效的。
空基优化就是这样一种技巧:在某些情况下,如果基类为空(没有任何非静态数据成员),那么编译器可能会省略其内存表示。
显然,标准似乎禁止对数据成员进行这种优化,即即使数据成员为空,它仍然必须占据至少一个字节的位置:来自 n3225,[class]
4 - 类类型的完整对象和成员子对象应具有非零大小。
注意:这会导致在策略设计中使用私有继承,以便在适当的时候启动 EBO
我想知道,使用 as-if 规则是否仍然可以执行此优化。
编辑:遵循一些答案和 cmets,以便更清楚地了解我在想什么。
首先,我举个例子:
struct Empty {};
struct Foo { Empty e; int i; };
我的问题是,为什么是 sizeof(Foo) != sizeof(int) ?特别是,除非您指定一些包装,否则由于对齐问题,Foo 的大小可能是 int 的两倍,这看起来很可笑。
注意:我的问题不是为什么是sizeof(Foo) != 0,这实际上也不是 EBO 所要求的
根据 C++,这是因为没有子对象的大小可能为零。然而,基地被授权具有零大小(EBO),因此:
struct Bar: Empty { int i; };
很可能(感谢 EBO)服从sizeof(Bar) == sizeof(int)。
Steve Jessop 似乎认为不会有两个子对象具有相同的地址。我考虑了一下,但在大多数情况下它实际上并没有阻止优化:
如果你有“未使用”的内存,那么它是微不足道的:
struct UnusedPadding { Empty e; Empty f; double d; int i; };
// chances are that the layout will leave some memory after int
但事实上,它甚至比这更“糟糕”,因为永远不会写入 Empty 空间(如果 EBO 启动,你最好不要...),因此你实际上可以将它放在一个占用的地方不是另一个对象的地址:
struct Virtual { virtual ~Virtual() {} Empty e; Empty f; int i; };
// most compilers will reserve some space for a virtual pointer!
或者,即使在我们原来的情况下:
struct Foo { Empty e; int i; }; // deja vu!
如果我们想要的只是不同的地址,可以使用(char*)foo.e == (char*)foo.i + 1。
【问题讨论】:
-
看看 Boost 的 Compressed Pair 库,看看如何获得这种优化。
-
@GMan:他们巧妙地使用了 EBO。但实际上 EBO 的这种使用正是促使我提出问题的原因。
-
从 9.2.12 开始“以后的成员在类对象中有更高的地址”,所以你的
(char*)foo.e == (char*)foo.i + 1不太有效;-P -
“如果我们想要的只是不同的地址” - 我认为我们不仅想要不同的地址,我们还想要不重叠的对象。其中“我们”是标准委员会。我认为您的虚拟案例是可以的,只要将空对象定义为虚拟对象的开头,或者在类中的另一个位置,其中的字节还不是任何子对象的一部分。您也可以在空成员的任何一侧都有访问说明符的地方玩这种技巧,因为那样它就不必与其他成员按顺序排列。