Czy to stary konstruktor w stylu C ++?

17

Oto kawałek kodu C ++.

W tym przykładzie wiele bloków kodu wygląda jak wywołania konstruktora. Niestety, kod blokowy nr 3 nie jest (możesz to sprawdzić za pomocą https://godbolt.org/z/q3rsxn i https://cppinsights.io ).

Myślę, że jest to stara notacja C ++, która mogłaby wyjaśnić wprowadzenie nowej notacji konstrukcyjnej C ++ 11 za pomocą {} (por. 4).

Czy masz wyjaśnienie T(i)znaczenia, tak bliskie notacji konstruktora, ale zdecydowanie tak różne?

struct T {
   T() { }
   T(int i) { }
};

int main() {
  int i = 42;
  {  // #1
     T t(i);     // new T named t using int ctor
  }
  {  // #2
     T t = T(i); // new T named t using int ctor
  }
  {  // #3
     T(i);       // new T named i using default ctor
  }
  {  // #4
     T{i};       // new T using int ctor (unnamed result)
  }
  {  // #5
     T(2);       // new T using int ctor (unnamed result)
  }
}

NB: w ten sposób T(i)(# 3) jest równoważne T i = T();

Pascal H.
źródło
1
Myślę, że wszystkie twoje stwierdzenia są poprawne.
Arne J
Zauważ, że kompilator powie ci wszystko, co powinieneś wiedzieć, jeśli po prostu o to zapytasz: Dodaj -Walli otrzymaszwarning: parentheses were disambiguated as redundant parentheses around declaration of variable named 'i' [-Wvexing-parse]” z clang lub nieco mniej „ warning: unnecessary parentheses in declaration of 'i' [-Wparentheses]” motywacji z gcc .
Max Langhof,
@QuentinUK dziękuję za ten link. Wiedziałem o tym o funkcjach (np. T t()), Ale nie o tak prostych wyrażeniach deklaracyjnych. Na pewno może to być irytujące .
Pascal H.,

Odpowiedzi:

17

Twierdzenie:

T(i);

jest równa:

T i;

Innymi słowy, deklaruje zmienną o nazwie itype T. Wynika to z faktu, że w niektórych miejscach dozwolone są nawiasy w deklaracjach (w celu zmiany powiązania deklaratorów), a ponieważ instrukcja ta może być analizowana jako deklaracja, jest to deklaracja (chociaż może mieć większy sens jako wyrażenie).

Brian
źródło
Czy to tylko wybór interpretacji odziedziczony ze specyfikacji C, gdzie int(i)również deklaruje intnazwę i?
Pascal H.,
@PascalH. Z pewnego punktu widzenia może to być prawda. Stroustrup napisał w D&E, że rozważał alternatywną, bardziej intuicyjną składnię deklaracji dla C ++. Gdyby C ++ nie musiał być kompatybilny wstecz z C, to być może miałby alternatywną składnię, a tym samym unikałby potencjalnej niejednoznaczności z wyrażeniami.
Brian
-1

Możesz użyć Eksploratora kompilatorów, aby zobaczyć, co dzieje się w asemblerze.

Widać, że # 1, # 2 # 4 i # 5 robią to samo, ale dziwnie # 3 wywołują drugi konstruktor (konstruktor obiektów podstawowych).

Czy ktoś ma wyjaśnienie?

Kod asemblera:

::T() [base object constructor]:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        nop
        pop     rbp
        ret
T::T(int):
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     DWORD PTR [rbp-12], esi
        nop
        pop     rbp
        ret
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 42
// #1
        mov     edx, DWORD PTR [rbp-4]
        lea     rax, [rbp-7]
        mov     esi, edx
        mov     rdi, rax
        call    T::T(int)
// #2
        mov     edx, DWORD PTR [rbp-4]
        lea     rax, [rbp-8]
        mov     esi, edx
        mov     rdi, rax
        call    T::T(int)
// #3
        lea     rax, [rbp-9]
        mov     rdi, rax
        call    T::T() [complete object constructor]
// #4
        mov     edx, DWORD PTR [rbp-4]
        lea     rax, [rbp-6]
        mov     esi, edx
        mov     rdi, rax
        call    T::T(int)
// #5
        lea     rax, [rbp-5]
        mov     esi, 2
        mov     rdi, rax
        call    T::T(int)

        mov     eax, 0
        leave
        ret

Matthieu H.
źródło