C++ 使用结构自身的数据成员聚合结构的初始化

C++ 使用结构自身的数据成员聚合结构的初始化,c++,aggregate-initialization,C++,Aggregate Initialization,这是关于这个的第n个问题,但我找不到确切的副本 假设以下代码: #include <iostream> struct S { int x; int y; }; class C { public: S s; C() : s{123, s.x} {} }; int main() { std::cout << C().s.y << '\n'; } #包括 结构{ int x; int-y; }; C类{ 公众: S

这是关于这个的第n个问题,但我找不到确切的副本

假设以下代码:

#include <iostream>

struct S {
    int x;
    int y;
};

class C {
public:
    S s;
    C() : s{123, s.x} {}
};

int main() {
     std::cout << C().s.y << '\n';
}
#包括
结构{
int x;
int-y;
};
C类{
公众:
S S;
C():s{123,s.x}{}
};
int main(){
std::cout来自C++14

8.5.1骨料[dcl.初始骨料]

1聚合是一个数组或类(第9条),没有用户提供的构造函数(12.1),也没有私有或非私有构造函数 受保护的非静态数据成员(第11条),无基类(第10条),无虚拟函数(第10.3条)

2当聚合由初始值设定项列表初始化时,如8.5.4所述,初始值设定项列表的元素 以递增下标或成员顺序作为聚合成员的初始值设定项

这意味着先用123初始化s.x,然后用s.x初始化s.y

如果没有优化,GCC 6.3将生成

C::C():
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8] # read address of s
        mov     DWORD PTR [rax], 123   # write 123 to s.x (offset 0 from s)
        mov     rax, QWORD PTR [rbp-8] # read address of s again
        mov     edx, DWORD PTR [rax]   # read contents of s.x to edx
        mov     rax, QWORD PTR [rbp-8] # read address of s
        mov     DWORD PTR [rax+4], edx # write s.y (offset 4 from s)
        nop
        pop     rbp
        ret

这与标准所说的是一致的。

虽然似乎没有明确规定这种把戏是不正确的,但它不足以有一个明确定义的行为

我认为它在评估顺序方面存在一些问题:

定义大括号列表中表达式的求值顺序;当然,成员初始化也有顺序

可以肯定地说,每个结构成员都是在对括号列表中的相应表达式求值之后初始化的(显然,大括号列表中的
s.x
是在初始化
s.y
之前求值的)

但是,似乎没有任何规则规定在计算括号列表的第二个元素之前必须初始化
s.x
,例如,程序甚至可以在开始初始化结构字段之前计算括号列表中的所有表达式

当然,没有规则并不容易证明,但如果没有规则,它看起来就像UB


UPD:确实与我的答案中缺少的内容非常相似,也许它毕竟不是UB。

我知道反对这种事情的理由在这里并不适用,因为
s
是微不足道的。同样,似乎对你有利。与优化相关的呢?通过优化,大部分代码都会被删除,包括s.y.a结果几乎是“std::cout,它们被当作初始化器并不一定意味着初始化也以相同的顺序执行。标准的这一部分说的是编译器必须遵守结构定义中出现的
x
y
的顺序(“以递增的成员顺序”))。编译器对代码的实际操作并不是任何语言律师问题的合适证据。如果该标准实际上规定了特定的初始化顺序,那么它必须在其他地方这样说。我不太理解这条规则。是否“take”是否意味着元素都被计算并用作初始值设定项?我的理解似乎是,成员字段是从init list元素表达式复制初始化的。