Это старый конструктор в стиле C ++?

17

Здесь кусок кода C ++.

В этом примере многие блоки кода выглядят как вызовы конструктора. К сожалению, блочного кода № 3 нет (это можно проверить с помощью https://godbolt.org/z/q3rsxn и https://cppinsights.io ).

Я думаю, что это старая нотация C ++, и она может объяснить введение новой нотации конструкции C ++ 11 с использованием {} (cf # 4).

У вас есть объяснение T(i)значения, настолько близкое к обозначению конструктора, но определенно такое отличающееся?

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: таким образом, T(i)(# 3) эквивалентно T i = T();

Паскаль Х.
источник
1
Я думаю, что все ваши заявления верны.
Арне Дж
Обратите внимание, что компилятор скажет вам почти все, что вам нужно знать, если вы просто спросите его: добавьте, -Wallи вы получите " warning: parentheses were disambiguated as redundant parentheses around declaration of variable named 'i' [-Wvexing-parse]" из clang или чуть менее мотивированный " warning: unnecessary parentheses in declaration of 'i' [-Wparentheses]" из gcc .
Макс
@QuentinUK спасибо за эту ссылку. Я знал об этом о функциях (например T t()), но не о столь простых выражениях объявлений. Конечно, это может быть неприятно .
Паскаль Х.

Ответы:

17

Заявление:

T(i);

эквивалентно:

T i;

Другими словами, он объявляет переменную с именем iтипа T. Это связано с тем, что в некоторых местах в объявлениях допускаются скобки (для изменения привязки объявлений), и поскольку этот оператор может быть проанализирован как объявление, он является объявлением (даже если он может иметь больше смысла как выражение).

Брайан
источник
Итак, это только выбор интерпретации, унаследованный от спецификаций C, где int(i)также объявляется intименованный i?
Паскаль Х.
@PascalH. С определенной точки зрения это может быть правдой. Страуструп написал в D & E, что он рассматривал альтернативный, более интуитивный синтаксис объявления для C ++. Если бы C ++ не должен был быть обратно совместимым с C, то, возможно, он имел бы альтернативный синтаксис и, таким образом, избежал бы потенциальной неоднозначности с выражениями.
Брайан
-1

Вы можете использовать Compiler Explorer, чтобы увидеть, что происходит в ассемблере.

Вы можете видеть, что # 1, # 2 # 4 и # 5 делают то же самое, но странно # 3 вызывают другой конструктор (конструктор базового объекта).

У кого-нибудь есть объяснение?

Код ассемблера:

::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
Матье Н
источник