空数据成员优化:是否可能?

Mat*_* M. 15 c++ compiler-optimization

在C++中,大多数优化都是从as-if规则派生的.也就是说,只要程序表现得如果没有进行优化,那么它们就是有效的.

空基优化就是这样一个技巧:在某些情况下,如果基类为空(没有任何非静态数据成员),则编译器可能会忽略其内存表示.

显然,似乎标准禁止对数据成员进行优化,即使数据成员为空,它仍然必须至少占用一个字节的位置:来自n3225,[class]

4 - 类类型的完整对象和成员子对象应具有非零大小.

注意:这导致在Policy Design中使用私有继承,以便在适当的时候启动EBO

我想知道,使用as-if规则,是否仍然可以执行此优化.


编辑:遵循一些答案和评论,并使我更清楚我想知道的.

首先,让我举一个例子:

struct Empty {};

struct Foo { Empty e; int i; };
Run Code Online (Sandbox Code Playgroud)

我的问题是,为什么sizeof(Foo) != sizeof(int)?特别是,除非你指定一些打包,否则很可能是因为对齐问题Foo将是int的两倍,这似乎是荒谬的夸大其词.

注意:我的问题不是原因sizeof(Foo) != 0,EBO实际上也不需要这样做

根据C++,这是因为没有子对象可能具有零大小.但是,基础被授权为零大小(EBO)因此:

struct Bar: Empty { int i; };
Run Code Online (Sandbox Code Playgroud)

很可能(感谢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
Run Code Online (Sandbox Code Playgroud)

但事实上,它甚至比那更"糟糕",因为Empty空间永远不会被写入(如果EBO开始......你最好不要这样做),因此你实际上可以把它放在一个不是另一个地址的占用地方宾语:

struct Virtual { virtual ~Virtual() {} Empty e; Empty f; int i; };
// most compilers will reserve some space for a virtual pointer!
Run Code Online (Sandbox Code Playgroud)

或者,即使在我们原来的情况下:

struct Foo { Empty e; int i; }; // deja vu!
Run Code Online (Sandbox Code Playgroud)

(char*)foo.e == (char*)foo.i + 1如果我们想要的只是不同的地址,那就可能有.

Ozi*_*rus 7

它即将使用该[[no_unique_address]]属性进入 c++20 。

提案P0840r2已被纳入标准草案。它有这个例子:

template<typename Key, typename Value, typename Hash, typename Pred, typename Allocator>
class hash_map {
  [[no_unique_address]] Hash hasher;
  [[no_unique_address]] Pred pred;
  [[no_unique_address]] Allocator alloc;
  Bucket *buckets;
  // ...
public:
  // ...
};
Run Code Online (Sandbox Code Playgroud)


Ste*_*sop 6

在as-if规则下:

struct A {
    EmptyThing x;
    int y;
};

A a;
assert((void*)&(a.x) != (void*)&(a.y));
Run Code Online (Sandbox Code Playgroud)

不得触发断言.因此x,当你只需要在结构中添加填充时,我没有看到秘密制作大小为0的任何好处.

我认为理论上编译器可以跟踪是否可以将指针带到成员,并且只有在它们肯定不是时才进行优化.这将是有限的用途,因为有两个不同版本的结构具有不同的布局:一个用于优化的情况,一个用于通用代码.

但是,例如,如果您在堆栈上创建A的实例,并使用完全内联(或优化器可见)的内容执行某些操作,是的,可以完全省略结构的某些部分.这不是特定于空对象的 - 空对象只是对象的特殊情况,其存储不被访问,因此在某些情况下根本不会被分配.

  • @MatthieuM."_unless完全内联,无法猜测数据成员的地址是否会被使用_"并且你可能不希望你的类的表示在某些成员函数实现发生变化时可能会改变! (2认同)