为什么C ++ 11的POD“标准布局”定义是这样的?

我正在研究C ++ 11中新的轻松的POD定义(第9.7节)

标准布局类是一个类:

  • 没有types非标准布局类(或这种types的数组)的非静态数据成员或引用,
  • 没有虚函数(10.3),没有虚基类(10.1),
  • 对所有非静态数据成员都具有相同的访问控制 (第11章)
  • 没有非标准的布局基类,
  • 在大多数派生类中最多有一个非静态数据成员最多有一个基类具有非静态数据成员 ,或者没有包含非静态数据成员的基类,
  • 没有与第一个非静态数据成员相同types的基类。

我已经突出了一些让我感到惊讶的东西。

如果我们容忍具有不同访问控制的数据成员,会出现什么问题呢?

如果第一个数据成员也是基类,会出现什么问题呢? 即

struct Foo {}; struct Good : Foo {int x; Foo y;}; struct Bad : Foo {Foo y; int x;}; 

我承认这是一个奇怪的build筑,但为什么Bad应该禁止,但Good

最后,如果不止一个组织成员有数据成员,会出现什么问题?

您可以将标准布局类对象地址转换为指向其第一个成员的指针,然后通过后面的段落之一返回,这通常也是在C:

 struct A { int x; }; A a; // "px" is guaranteed to point to ax int *px = (int*) &a; // guaranteed to point to a A *pa = (A*)px; 

为此,第一个成员和完整的对象必须有相同的地址(编译器不能调整int指针的任何字节,因为它不知道它是否是A的成员)。

最后,如果不止一个组织成员有数据成员,会出现什么问题?

在一个class级内,按照申报单的顺序分配成员地址。 但是,C ++不会规定跨类的数据成员的分配顺序。 如果派生类和基类都具有数据成员,则标准没有有意地定义它们的地址的顺序,从而使得实现在布置存储器方面具有完全的灵活性。 但是对于上面的演员来说,你需要知道什么是分配顺序中的“第一”成员!

如果第一个数据成员也是基类,会出现什么问题呢?

如果基类具有与第一个数据成员相同的types,那么将基类放在内存中的派生类对象之前的实现将需要在内存中的派生类对象数据成员之前具有填充字节(基类的大小为1 ),以避免基类和第一个数据成员具有相同的地址(在C ++中,同一types的两个不同的对象总是具有不同的地址)。 但是,这又会使派生类对象的地址不能转换为其第一个数据成员的types。

这基本上是关于与C + + 03和C的兼容性:

  • 相同的访问控制 – C ++ 03实现允许使用访问控制指定符作为重新sorting类的(成员)组的机会,例如为了更好地打包它。
  • 在具有非静态数据成员的层次结构中多于一个类–C ++ 03没有说明基类所在的位置,或者在基类子对象中是否消除了填充,这些子对象将出现在相同types的完整对象中。
  • 基类和同一types的第一个成员 – 由于第二个规则,如果基类types用于数据成员,则它必须是空类。 许多编译器都实现了空基类优化,所以Andreas所说的具有相同地址的子对象将是真实的。 我不确定标准布局类是什么,这意味着基类子对象具有与同一types的第一个数据成员相同的地址是不好的,但是当基类子对象具有相同的地址与不同types的第一个数据成员相同的地址。 [编辑:这是因为相同types的不同对象具有不同的地址,即使它们是空的子对象。 感谢Johannes]

C ++ 0x可能已经定义了这些东西是标准布局types,在这种情况下,它也会定义它们的布局方式,与标准布局types相同。 约翰内斯的回答更进一步,看看他的例子,标准布局类的一个不错的属性,这些事情干扰。

但是如果这样做的话,那么一些实现将被迫改变它们如何布置类来匹配新的需求,这对C ++ 0x之前和之后的编译器的不同版本之间的结构兼容性造成麻烦。 它基本上打破了C ++ ABI。

我对如何定义标准布局的理解是,他们研究了什么POD需求可以放松而不破坏现有的实现。 所以我假设没有检查,以上是一些现有的C ++ 03实现确实使用类的非POD性质来做一些与标准布局不兼容的例子。

如果我们容忍具有不同访问控制的数据成员,会出现什么问题呢?

目前的语言表示,编译器不能在同一访问控制下对成员重新sorting。 喜欢:

 struct x { public: int x; int y; private: int z; }; 

这里x必须在y之前分配,但是对x和y没有限制。

 struct y { public: int x; public: int y; }; 

新的措辞表明,尽pipe有两个publicy仍然是POD。 这实际上是规则的放松。

至于为什么Bad不允许让我从我发现的文章quoute:

这确保两个具有相同类types并且属于相同的最大派生对象的子对象不被分配在相同的地址处。

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2172.html

从项目符号5看来,由于大多数派生类都有非静态数据成员(int),所以它们看起来都是非pod,它不能拥有带有非静态数据成员的基类。

我理解为:“只有一个”基本“类(即类本身或它inheritance的类之一)可以有非静态数据成员”

struct Good也不是标准布局,因为Foo和Good都有非静态数据成员。

这样,好应该是:

 struct Foo {int foo;}; struct Good : public Foo {Foo y;}; 

这不能满足第六个子弹。 因此,第六个子弹?