Ist das ein alter Konstruktor im C ++ - Stil?

17

Hier ein Stück C ++ - Code.

In diesem Beispiel sehen viele Codeblöcke wie Konstruktoraufrufe aus. Blockcode 3 ist dies leider nicht (Sie können ihn mit https://godbolt.org/z/q3rsxn und https://cppinsights.io überprüfen ).

Ich denke, es ist eine alte C ++ - Notation und könnte die Einführung der neuen C ++ 11-Konstruktionsnotation mit {} erklären (vgl. # 4).

Haben Sie eine Erklärung für die T(i)Bedeutung, die einer Konstruktornotation so nahe kommt, aber definitiv so anders ist?

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: somit ist T(i)(# 3) äquivalent zu T i = T();

Pascal H.
quelle
1
Ich denke, alle Ihre Aussagen sind richtig.
Arne J
Beachten Sie, dass der Compiler Ihnen so ziemlich alles sagt, was Sie wissen müssen, wenn Sie nur danach fragen: Hinzufügen -Wallund Sie erhalten " warning: parentheses were disambiguated as redundant parentheses around declaration of variable named 'i' [-Wvexing-parse]" von clang oder das etwas weniger motivierte " warning: unnecessary parentheses in declaration of 'i' [-Wparentheses]" von gcc .
Max Langhof
@QuentinUK danke für diesen Link. Ich war mir dessen bewusst (z. B. T t()), aber nicht für so einfache Deklarationsausdrücke. Dies könnte sicherlich ärgerlich sein .
Pascal H.

Antworten:

17

Die Aussage:

T(i);

ist äquivalent zu:

T i;

Mit anderen Worten, es wird eine Variable mit dem Namen itype deklariert T. Dies liegt daran, dass in Deklarationen an einigen Stellen Klammern zulässig sind (um die Bindung von Deklaratoren zu ändern). Da diese Anweisung als Deklaration analysiert werden kann, handelt es sich um eine Deklaration (auch wenn sie als Ausdruck möglicherweise sinnvoller ist).

Brian
quelle
Ist dies also nur eine Interpretationsauswahl, die von C-Spezifikationen geerbt wurde, bei denen int(i)auch ein intName deklariert wird i?
Pascal H.
@PascalH. Unter einem bestimmten Gesichtspunkt könnte dies zutreffen. Stroustrup schrieb in D & E, dass er eine alternative, intuitivere Deklarationssyntax für C ++ in Betracht gezogen habe. Wenn C ++ nicht abwärtskompatibel mit C sein müsste, hätte es möglicherweise die alternative Syntax und vermeidet so die mögliche Mehrdeutigkeit mit Ausdrücken.
Brian
-1

Mit dem Compiler-Explorer können Sie sehen, was im Assembler passiert.

Sie können sehen, dass # 1, # 2 # 4 und # 5 dasselbe tun, aber seltsamerweise # 3 den anderen Konstruktor (den Basisobjektkonstruktor) aufrufen.

Hat jemand eine Erklärung?

Assembler-Code:

::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.
quelle