类/结构成员的地址排序

Bon*_*ero 7 c++ language-lawyer c++23

C++23 草案要求结构或类的后续非静态数据成员必须具有更高的地址。据我所知,早期的标准也部分地要求这样做,但是当允许编译器重新排序这些数据成员时有一些规则。这里有人能告诉我哪些规则在什么时候适用吗?

eer*_*ika 6

来自最新的 C++23 草案:

[expr.rel]

比较不相等指针与对象的结果是根据符合以下规则的偏序来定义的:

  • 如果两个指针指向同一对象的不同非静态数据成员,或者指向此类成员的子对象,则递归地,指向后来声明的成员的指针需要比较更大,前提是两个成员都不是零大小的子对象,并且它们的类是不是一个工会。

非静态成员的分配顺序是该规则的必然结果。该规则由注释引用:

[class.mem.general]

[注 8:分配非零大小的非变体非静态数据成员 ([intro.object]),以便后面的成员在类对象 ([expr.rel]) 中具有更高的地址。


在 C++11-20 中,在提案 P1847 之前,规则是:

  • 如果两个指针指向同一对象的不同非静态数据成员,或者指向此类成员的子对象,则递归地,如果这两个成员具有相同的访问控制([class. access]),两个成员都不是零大小的子对象,并且它们的类不是联合。
  • 否则,两个指针都不需要比较大于另一个指针

在 C++23 之前,编译器可以使用不同的访问控制对成员进行重新排序。例子:

struct s {
    int a;
protected:
    int b;
};

s ab;
// value of c is implementation dependent in C++20
// value of c is true in C++23
bool c = &ab.a < &ab.b;
Run Code Online (Sandbox Code Playgroud)

主要编译器实际上没有使用除声明顺序之外的任何其他内容。

C++03 甚至更加轻松,尽管很微妙:

[类.mem]

分配未插入访问说明符声明的(非联合)类的非静态数据成员,以便后面的成员在类对象中具有更高的地址。由访问说明符分隔的非静态数据成员的分配顺序未指定(11.1)。


[expr.rel]

  • 如果两个指针指向同一对象的非静态数据成员,或者指向此类成员的子对象或数组元素,则指向后面声明的成员的指针会比较更大,前提是这两个成员没有由访问说明符标签分隔( 11.1) 并且前提是他们的班级不是工会。
  • 如果两个指针指向由访问说明符标签 (11.1) 分隔的同一对象的非静态数据成员,则结果未指定。