空派生优化 [英] Empty derived optimization

查看:165
本文介绍了空派生优化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大多数C ++程序员都知道空基类优化为技术 / 成语空的子类会发生什么?例如

  class EmptyBase {
int i;
};

template< typename T>
class Derived:T {
};

std :: cout<< sizeof(Derived< EmptyBase>); //有这个标准的verdic吗?

与EBO类似,应该有一个EDO声明,因为派生类不会提供任何更多的成员,也不向其参数化类型引入任何虚拟的成员,它不应该需要更多的内存。考虑各种情况下,类似的东西可能apper(多继承,单继承...):




  • 这样的优化标准/可能?

  • 如果是,这种优化的机制是什么?是类似于 EBO's



注意:使用从其参数化类型派生的类模板是相当典型的。

解决方案

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


[A]大多数派生对象应具有非零大小,更多
字节的存储空间。基类子对象的大小可能为零。


和:


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

blockquote>

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


这从来没有说过,只有空基地适合任何种类的布局更改和离开有足够的空间用于挤压布局:例如:

  struct A {}; struct B:A {int x; }; //equivalentto {int} 

struct X {int a; }; struct Y:X {}; //等价于{int}

考虑 B b; code>和 Y y; 。可能的是 b 的地址, A -subobject的 b (即& static_cast< A&>(b))和 bx 是相同的,类似地, y 的地址, X -suboject y ,并且 ya 的地址是相同的。



不工作是这样:

  struct S {}; struct T {S s; }; //equivalentto {char} 

code>我的意思是最明智的,节省空间的实现。



更有趣的情况如下:

  struct Foo {int x; char a; }; // {int,char,char [3]} 

struct Bar:Foo {short q; }; // {int,char,char,short}



此示例假设 (int)== 4 sizeof(short)== 2 。我们有 sizeof(Foo)== 8 为了对齐的原因,但 sizeof(Bar)也是<$ c $






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


分配具有相同访问控制(非11)的(非联合)类的非静态数据成员,成员在类对象中具有较高的地址。未指定具有不同访问控制的非静态数据成员的分配顺序(11)。实施对齐要求可能导致两个相邻成员不能彼此紧接着分配;因此可能需要管理虚拟函数(10.3)和虚拟基类(10.1)的空间。


标准布局类在开始没有填充,使得它们的地址等于它们的初始成员的地址。由于标准布局要求所有基本都是空的,或者最派生类本身没有数据成员,这意味着标准布局类必须使用一种空基优化,上面的 B Y 的初始部分实际上是强制性的。


Most C++ programmers know about the empty base class optimazation as a technique/idiom. What happens with empty child classes? For example

class EmptyBase {
    int i;
};

template<typename T>
class Derived : T { 
};

std::cout << sizeof(Derived<EmptyBase>); // Is there a standard verdic on this?

Similarly to the EBO there should be an EDO stating that since a derived class doesn't provide any more members, nor introduces any virtual ones to its parametrizing type, it should not require more memory. Considering the various situations in which something like that might apper (multiple inheritance, single inheritance ...) :

  • Is such an optimization standard/possible?
  • If yes, what are the mechanics of such an optimization, are they similar to EBO's?

Note: Using class templates that derive from their parameterizing types is fairly typical. The topic is about space wasting in such situations

解决方案

The standard does not contain an "empty base class" case per se. Rather, it says (cf. 1.8):

[A] most derived object shall have a non-zero size and shall occupy one or more bytes of storage. Base class subobjects may have zero size.

And:

Unless an object is [...] a base class subobject of zero size, the address of that object is the address of the first byte it occupies. Two objects [...] may have the same address if one is a subobject of the other, or if at least one is a base class subobject of zero size and they are of different types; otherwise, they shall have distinct addresses.

And (Clause 9):

Complete objects and member subobjects of class type shall have nonzero size. Footnote: Base class subobjects are not so constrained.

This never says that only empty bases are amenable to any kind of layout change and leaves ample room for "squeezing" the layout: for example:

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

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

Consider B b; and Y y;. it is possible that the address of b, that of the A-subobject of b (i.e. &static_cast<A&>(b)) and that of b.x are the same, and similarly that the address of y, the X-suboject of y, and the address of y.a are the same.

The only thing that does not work is this:

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

By "equivalent" I mean the most sensible, space-saving implementation.

A more interesting case is the following:

struct Foo { int x; char a; };                    // { int, char, char[3] }

struct Bar : Foo { short q; };                    // { int, char, char, short }

This example assumes sizeof(int) == 4 and sizeof(short) == 2. We have sizeof(Foo) == 8 for alignment reasons, but sizeof(Bar) is also 8 despite having more data members.


Another relevant part of the standard is 9.2/13:

Nonstatic data members of a (non-union) class with the same access control (Clause 11) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified (11). Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (10.3) and virtual base classes (10.1).

Finally, 9.2/10 says that standard-layout classes have no padding at the beginning, so that their address equals the address of their "initial member". Since standard-layout requires that all bases either be empty, or that the most-derived class itself have no data members, this means that standard-layout classes must employ a kind of "empty base" optimization, and the initial part of the layout of my B and Y above is actually mandatory.

这篇关于空派生优化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆