2017-04-05 18 views
12

Jest to n-ty pytanie o to, ale nie mogłem znaleźć dokładną kopią ...Kruszywo inicjalizacji struct, używając własnych członków danych

Załóżmy następujący kod:

#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'; 
} 

Czy można w taki sposób zainicjować s.y? (tylko ReSharper JetBrains narzeka na to z: Object member this->s.x might not be initialized).

Byłoby wspaniale, gdyby ktoś potwierdził swoją odpowiedź cytatem ze standardu.

+0

The [najbliższa reguła] (http://eel.is/c++draft/special#class.cdtor-1) Wiem, że przeciwko temu nie ma tu zastosowania, ponieważ 'S' jest trywialne. Ponownie, [ta reguła] (http: // eel.to/C++ draft/dcl.init.aggr # 3) wydaje się faworyzować ciebie. – WhiZTiM

+0

Powiązane [Czy zdefiniowano zachowanie odwołujące się do wczesnego członka z późniejszego wyrażenia członka podczas inicjowania agregacji?] (Http://stackoverflow.com/q/32940847/1708801) –

Odpowiedz

1

z C++ 14

8.5.1 kruszywa [dcl.init.aggr]

1 Agregat jest tablicą lub klasy (Rozdział 9) bez konstruktorów dostarczane przez użytkowników (12.1) , żaden prywatny ani nie chronił niestatycznych członków danych (klauzula 11), żadnych klas bazowych (klauzula 10) i żadnych funkcji wirtualnych (10.3).

2 Kiedy agregat jest inicjowany przez listę inicjalizującą, jak określono w 8.5.4, elementy listy inicjalizacyjnej są traktowane jako inicjalizatory dla członków agregatu, w rosnącym indeksie dolnym lub członku.

Oznacza to, że s.x jest najpierw inicjowany przez 123, a następnie s.y jest inicjowany przez s.x.

Bez optymalizacji GCC 6.3 generuje

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 

która zgadza się z tym, co mówi normy.

+0

A co z optymalizacjami? – WhiZTiM

+0

Przy optymalizacji większość kodu zostaje usunięta, w tym sy, a wynik jest prawie "std :: cout << 123 << '\ n'; –

+1

To, że * zostały * pobrane * jako inicjatory niekoniecznie oznacza, że ​​inicjalizacja jest również wykonywana w tej samej kolejności.Jak ta część standardu mówi, że kompilator musi przestrzegać kolejności 'x' i' y', jak pojawiają się w definicji struktury ("w rosnącej kolejności członów"). tak naprawdę z kodem nie jest odpowiednim dowodem na jakiekolwiek pytanie językowo-prawnicze.Jeśli standard faktycznie nakazuje określoną kolejność inicjalizacji, to musi to powiedzieć gdzie indziej. –

1

Chociaż wydaje się, że nie ma żadnej reguły, która wyraźnie stwierdza, że ​​ta sztuczka jest źle sformułowana, to nie wystarczy, aby miała dobrze określone zachowanie.

myślę, że ma pewne problemy z rzędu ocena:

this rule określa kolejność obliczeń dla wyrażenia w liście usztywnione; Oczywiście istnieje również inicjalizacja członkostwa.

Można bezpiecznie powiedzieć, że każdy element struct jest inicjowany po ocenie odpowiedniego wyrażenia na liście nawiasów (oczywiście s.x na liście usztywnionej jest oceniany przed zainicjowaniem s.y).

Jednak wydaje się, że nie ma żadnej reguły, która określałaby, że s.x w twoim przypadku musi zostać zainicjowane przed oceną drugiego elementu listy usztywnionej, np. program może ocenić wszystkie wyrażenia na liście nawiasów przed rozpoczęciem inicjalizacji pól struct.

Oczywiście brak reguły nie jest łatwy do udowodnienia, ale jeśli jej nie ma, wygląda na UB.

UPD: the rule from @PaulFloyd's answer rzeczywiście bardzo przypomina to, czego brakowało w mojej odpowiedzi, może to wcale nie jest UB.