¿Es este un antiguo constructor de estilo C ++?

17

Aquí una pieza de código C ++.

En este ejemplo, muchos bloques de código parecen llamadas de constructor. Desafortunadamente, el código de bloqueo n. ° 3 no lo es (puede verificarlo usando https://godbolt.org/z/q3rsxn y https://cppinsights.io ).

Creo que es una notación antigua de C ++ y podría explicar la introducción de la nueva notación de construcción C ++ 11 usando {} (cf # 4).

¿Tiene una explicación para el T(i)significado, tan cerca de una notación de constructor, pero definitivamente tan diferente?

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: por lo tanto, T(i)(# 3) es equivalente a T i = T();

Pascal H.
fuente
1
Creo que todas tus declaraciones son correctas.
Arne J
Tenga en cuenta que el compilador le dirá casi todo lo que necesita saber si solo lo pregunta: agregue -Wally obtenga " warning: parentheses were disambiguated as redundant parentheses around declaration of variable named 'i' [-Wvexing-parse]" de clang, o " warning: unnecessary parentheses in declaration of 'i' [-Wparentheses]" un poco menos motivado " de gcc .
Max Langhof
@QuentinUK gracias por este enlace. Estaba al tanto de las funciones (por ejemplo T t()), pero no de expresiones de declaración tan simples. Por supuesto, esto podría ser irritante .
Pascal H.

Respuestas:

17

La declaración:

T(i);

es equivalente a:

T i;

En otras palabras, declara una variable denominada icon tipo T. Esto se debe a que los paréntesis están permitidos en las declaraciones en algunos lugares (para cambiar el enlace de los declaradores) y dado que esta declaración se puede analizar como una declaración, es una declaración (aunque podría tener más sentido como expresión).

Brian
fuente
Entonces, ¿es esto solo una elección de interpretación heredada de las especificaciones C donde int(i)también se declara un intnombre i?
Pascal H.
@PascalH. Desde cierto punto de vista, eso podría ser cierto. Stroustrup escribió en D&E que había considerado una sintaxis de declaración alternativa más intuitiva para C ++. Si C ++ no hubiera tenido que ser retrocompatible con C, entonces quizás tendría la sintaxis alternativa, y así evitaría la posible ambigüedad con las expresiones.
Brian
-1

Puede usar Compiler Explorer para ver qué sucede en el ensamblador.

Puede ver que # 1, # 2 # 4 y # 5 hacen lo mismo pero estranguladamente # 3 llaman al otro constructor (el constructor de objetos base).

¿Alguien tiene una explicación?

Código de ensamblador:

::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
fuente