Este é um construtor de estilo C ++ antigo?

17

Aqui está um pedaço de código C ++.

Neste exemplo, muitos blocos de código parecem chamadas de construtor. Infelizmente, o código de bloco nº 3 não é (você pode verificá-lo usando https://godbolt.org/z/q3rsxn e https://cppinsights.io ).

Eu acho que é uma notação antiga do C ++ e poderia explicar a introdução da nova notação de construção do C ++ 11 usando {} (cf # 4).

Você tem uma explicação para o T(i)significado, tão próxima de uma notação de construtor, mas definitivamente tão 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: assim, T(i)(# 3) é equivalente a T i = T();

Pascal H.
fonte
11
Eu acho que todas as suas declarações estão corretas.
Arne J
Note que o compilador lhe dirá praticamente tudo o que você precisa saber se você apenas perguntar: "Adicionar -Walle você recebe " warning: parentheses were disambiguated as redundant parentheses around declaration of variable named 'i' [-Wvexing-parse]"do clang, ou o" warning: unnecessary parentheses in declaration of 'i' [-Wparentheses]" menos motivado" " do gcc .
precisa
@QuentinUK obrigado por este link. Eu sabia disso sobre funções (por exemplo T t()), mas não para expressões de declaração tão simples. Com certeza, isso pode ser irritante .
Pascal H.

Respostas:

17

A declaração:

T(i);

é equivalente a:

T i;

Em outras palavras, ele declara uma variável nomeada icom type T. Isso ocorre porque parênteses são permitidos nas declarações em alguns lugares (para alterar a ligação dos declaradores) e, como essa declaração pode ser analisada como uma declaração, é uma declaração (mesmo que faça mais sentido como expressão).

Brian
fonte
Então, isso é apenas uma opção de interpretação herdada das especificações C, onde int(i)também declara um intnome i?
Pascal H.
@PascalH. De um certo ponto de vista, isso poderia ser verdade. Stroustrup escreveu em D&E que considerara uma sintaxe de declaração alternativa e mais intuitiva para C ++. Se o C ++ não tivesse que ser retrocompatível com o C, talvez tivesse a sintaxe alternativa e, assim, evitasse a ambiguidade potencial das expressões.
27719 Brian
-1

Você pode usar o Compiler Explorer para ver o que acontece no assembler.

Você pode ver que os números 1, 2 e 4 fazem a mesma coisa, mas estranhamente o número 3 chama o outro construtor (o construtor do objeto base).

Alguém tem uma explicação?

Código do montador:

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