C++ 为什么C++;11';s POD“;“标准布局”;定义是什么?

C++ 为什么C++;11';s POD“;“标准布局”;定义是什么?,c++,c++11,standard-layout,C++,C++11,Standard Layout,我正在研究(第9.7节)中新的、宽松的POD定义 标准布局类是指: 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员 没有虚拟函数(10.3)和虚拟基类(10.1) 对所有非静态数据成员具有相同的访问控制(第11条) 没有非标准布局基类 在最派生的类中没有非静态数据成员,最多有一个基类具有非静态数据成员,或者没有基类具有非静态数据成员,以及 没有与第一个非静态数据成员类型相同的基类 我强调了让我吃惊的地方 如果我们容忍具有不同访问控制的数据成员,会出现什么问题 如果第一个数据成

我正在研究(第9.7节)中新的、宽松的POD定义

标准布局类是指:

  • 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员
  • 没有虚拟函数(10.3)和虚拟基类(10.1)
  • 对所有非静态数据成员具有相同的访问控制(第11条)
  • 没有非标准布局基类
  • 在最派生的类中没有非静态数据成员,最多有一个基类具有非静态数据成员,或者没有基类具有非静态数据成员,以及
  • 没有与第一个非静态数据成员类型相同的基类
我强调了让我吃惊的地方

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

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

struct Foo {};
struct Good : Foo {int x; Foo y;};
struct Bad  : Foo {Foo y; int x;};
我承认这是一个奇怪的结构,但是为什么应该禁止
坏的
,而不是
好的


最后,如果一个以上的组成类有数据成员,会出现什么问题?

从项目符号5来看,这两个类似乎都是非pod的,因为最派生的类有非静态数据成员(int),它不能有一个具有非静态数据成员的基类


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

至于为什么不允许
坏的
,让我从我发现的一篇文章中引出:

这可以确保两个子对象具有相同的类类型和 属于同一最派生对象的对象不会同时分配 同一地址


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

这样,好的应该是:

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

它不能满足第六个子弹的要求。因此,第六个要点?

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

  • 同样的访问控制-C++03实现允许使用访问控制说明符作为重新排序类(组)成员的机会,例如为了更好地打包它
  • 在具有非静态数据成员的层次结构中,不止一个类—C++03没有说明基类位于何处,也没有说明是否在基类子对象中省略了填充,这些子对象将出现在同一类型的完整对象中
  • 基类和同一类型的第一个成员-由于第二条规则,如果基类类型用于数据成员,则它必须是空类。许多编译器确实实现了空基类优化,因此Andreas关于具有相同地址的子对象的说法是正确的。我不确定标准布局类是什么意思,这意味着基类子对象与同一类型的第一个数据成员具有相同的地址是不好的,但基类子对象与不同类型的第一个数据成员具有相同的地址并不重要。[编辑:这是因为同一类型的不同对象有不同的地址,即使它们是空的子对象。感谢Johannes]
C++0x可能也可以定义这些东西是标准布局类型,在这种情况下,它还可以定义它们的布局方式,与标准布局类型的布局方式相同。Johannes的回答进一步深入了这一点,看看他的例子,这些东西干扰了标准布局类的一个很好的属性

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

我对标准布局是如何定义的理解是,他们研究了在不破坏现有实现的情况下可以放松哪些POD需求。因此,我假设以上是一些现有的C++03实现使用类的非POD性质来做一些与标准布局不兼容的事情的示例,而不进行检查

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

当前语言表示编译器不能在同一访问控制下对成员重新排序。比如:

struct x
{
public:
    int x;
    int y;
private:
    int z;
};
这里x必须在y之前分配,但是z相对于x和y没有限制

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

新的措辞说,尽管有两个公共的
s,但
y仍然是一个POD。这实际上是对规则的放松。

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

struct A { int x; };
A a;

// "px" is guaranteed to point to a.x
int *px = (int*) &a;

// guaranteed to point to a
A *pa = (A*)px; 
为此,第一个成员和完整对象必须具有相同的地址(编译器不能将int指针调整任何字节,因为它不知道它是否是
a
的成员)

最后,如果一个以上的组成类有数据成员,会出现什么问题

在一个类中,根据声明顺序以递增的地址分配成员。但是C++不指定数据成员跨类的分配顺序。如果派生类和基类都有数据成员,则标准不会故意为它们的地址定义顺序,以便在布局内存时为实现提供充分的灵活性。但要让上述演员阵容发挥作用,您需要知道分配顺序中的“第一个”成员是什么

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

如果基类的类型与第一个数据成员的类型相同,那么将基类放在内存中派生类对象之前的实现将