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

spr*_*aff 41 c++ pod standard-layout c++11

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

标准布局类是一个类:

  • 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
  • 没有虚函数(10.3),也没有虚基类(10.1),
  • 对所有非静态数据成员具有相同的访问控制(第11条),
  • 没有非标准布局基类,
  • 或者在最派生类中没有非静态数据成员,并且最多只有一个具有非静态数据成员的基类,或者没有具有非静态数据成员的基类,并且
  • 没有与第一个非静态数据成员相同类型的基类.

我突出了让我感到惊讶的一些事情.

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

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

struct Foo {};
struct Good : Foo {int x; Foo y;};
struct Bad  : Foo {Foo y; int x;};
Run Code Online (Sandbox Code Playgroud)

我承认这是一个奇怪的结构,但为什么要Bad被禁止但不是Good

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

Ste*_*sop 23

它主要是关于与C++ 03和C的兼容性:

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

的C++ 0x大概可以有定义的那些东西都是标准布局类型也是如此,在这种情况下,它也将确定他们是如何布局,以它为标准布局类型相同的程度.Johannes的答案进一步探讨了这一点,看看他的标准布局类的一个很好的属性的例子,这些东西会干扰.

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

我对如何定义标准布局的理解是,他们研究了在不破坏现有实现的情况下可以放松的POD要求.所以我假设没有检查,上面是一些示例,其中一些现有的C++ 03实现确实使用类的非POD特性来做一些与标准布局不兼容的事情.


Joh*_*itb 22

您可以将标准布局类对象地址强制转换为指向其第一个成员的指针,然后返回后面的段落之一,这通常也在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; 
Run Code Online (Sandbox Code Playgroud)

为了实现这一点,第一个成员和完整对象必须具有相同的地址(编译器不能通过任何字节调整int指针,因为它无法知道它是否是一个成员A).

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

在一个类中,成员根据声明顺序分配在增加的地址中.但是,C++并没有规定跨类的数据成员的分配顺序.如果派生类和基类都有数据成员,则标准不会故意定义其地址的顺序,以便为布局内存提供完全的灵活性.但是为了使上面的演员工作,您需要知道分配顺序中的"第一"成员是什么!

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

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


Bo *_*son 7

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

当前语言表示编译器无法在同一访问控制下重新排序成员.喜欢:

struct x
{
public:
    int x;
    int y;
private:
    int z;
};
Run Code Online (Sandbox Code Playgroud)

这里x必须在y之前分配,但z相对于x和y没有限制.

struct y
{
public:
    int x;
public:
    int y;
};
Run Code Online (Sandbox Code Playgroud)

新的措辞说y尽管有两个,但仍然是POD public.这实际上是对规则的放宽.