2017-04-05 25 views
12

這是關於這個第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'; 
} 

可以像這樣初始化s.y嗎? (只有JetBrains的ReSharper通過以下方式投訴:Object member this->s.x might not be initialized)。

如果有人用標準的引用來確認他們的答案,那將會很棒。

+0

[最接近的規則](http://eel.is/c++draft/special#class.cdtor-1)我知道這種事情在這裏不適用,因爲'S'是微不足道的。再次,[這個規則](http:// eel。是/ C++ draft/dcl.init.aggr#3)似乎有利於你。 – WhiZTiM

+0

相關[在聚合初始化過程中是否定義了從較新成員表達式引用早期成員的行爲?](http://stackoverflow.com/q/32940847/1708801) –

回答

1

從C++ 14

8.5.1聚集[dcl.init.aggr]

1所述的骨料是沒有用戶提供的構造的陣列或一個類(第9節)(12.1) ,沒有私有或受保護的非靜態數據成員(第11章),沒有基類(第10章),也沒有虛函數(10.3)。

2當按照8.5.4的規定初始化器列表初始化一個集合時,初始化器列表 的元素將作爲集合成員的初始化器,以下標或成員順序遞增。

這意味着s.x首先用123初始化,然後s.y用s.x初始化。

沒有優化,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 

與標準說什麼同意。

+0

那麼優化呢? – WhiZTiM

+0

隨着優化,大部分代碼被刪除,包括sy,結果幾乎是「std :: cout << 123 <<'\ n'; –

+1

*它們被作爲初始化者並不一定意味着初始化也是以相同的順序執行的,標準的這一部分說的是編譯器必須尊重「x」和「y」的順序,因爲它們出現在結構的定義中(「按增加成員順序」)。如果標準實際上要求一個特定的初始化順序,那麼它必須在其他地方說明。 –

1

儘管看起來沒有明確規定這個技巧是不合格的規則,但它不足以具有定義良好的行爲。

我認爲它有一些問題,評價的順序:

this rule定義評估在支撐列表中表達式的順序;當然,也有一個成員初始化的命令。

可以肯定地說,在對括號列表中的對應表達式進行評估之後,每個結構成員都被初始化(在初始化s.y之前,顯然支撐列表中的s.x被評估)。

然而,在評估支撐列表的第二個元素之前,似乎並沒有規定在你的情況下s.x必須被初始化,例如,程序甚至可以在開始初始化struct字段之前評估括號列表中的所有表達式。

當然,缺乏規則不容易證明,但如果它不在那裏,它看起來像UB。

UPDthe rule from @PaulFloyd's answer確實與我的答案中所缺少的東西非常相似,也許它畢竟不是UB。