Ответ 1
Ни один из них не является вызовом конструктора.
Первым является явное преобразование типов, которое создает объект типа std::list<int>
.
Второе - это определение переменной, которое создает объект типа std::list<int>
.
Конструктор по умолчанию (конструктор, не принимающий аргументов) в обоих случаях называется частью создания.
Хотя вы можете видеть, что такие вещи упоминаются как "вызовы конструктора", нет синтаксической конструкции для явного и однозначного вызова конструктора в С++.
Причина, по которой нужны скобки, когда другая не существует, состоит в том, что они представляют собой две отдельные языковые конструкции с различным синтаксисом, а не два способа вызова конструктора.
Обратите внимание, что если вы добавите круглые скобки во второй пример, вы фактически объявляете функцию, а не определяете переменную:
std::list<int> foo; //variable definition
std::list<int> foo(); //function taking no args, returning a std::list<int>
Это обычно называется most-vexing-parse. С++ 11 ввел исправленную инициализацию, чтобы обойти это:
std::list<int> foo{}; //variable definition
Стандарт, для тех, кто склонен
(Цитаты из N3337)
"Но T()
обязательно выглядит как вызов конструктора, почему это не так?"
В этом контексте T()
называется явным преобразованием типа с функциональными обозначениями:
5.2.3 Явное преобразование типа (функциональная нотация) [expr.type.conv]
1 [...]
2 Выражение
T()
, гдеT
- спецификатор простого типа или typename-specifier для типа объекта без массива или (возможно, cv-qualified) тип void, создает значение prvalue указанного типа, который инициализируется значением (8.5; инициализация не выполняется для случая void()). [Примечание: еслиT
- тип неклассов, который cv-qualified, cv-квалификаторы игнорируются при определении типа полученного prvalue (3.10). -end note]
Таким образом, это создает значение prvalue, которое инициализируется значением.
[dcl.init]/7:
Для инициализации объекта с типомT
означает:- , если T - тип класса (возможно, cv-grade) (раздел 9) с предоставленным пользователем конструктором (12.1), тогда конструктор по умолчанию для T называется (и инициализация плохо сформирован, если T не имеет доступных значений по умолчанию Конструктор);
- [...]
Таким образом, это вызывает конструктор как часть инициализации значения, которая является частью явного преобразования типов. Как указано выше, невозможно напрямую вызвать конструктор. В стандарте говорится:
[class.ctor]/1:
Конструкторы не имеют имен. Для объявления или определения конструктора используется специальный синтаксис декларатора. Синтаксис использует:- необязательный spec-specifier-seq, в котором каждый spec-спецификатор является либо спецификатором функции, либо constexpr,
- имя класса конструкторов и
- список параметров
в этом порядке. В таком объявлении необязательные круглые скобки вокруг имени класса конструктора игнорируются.
Поэтому у конструкторов нет имен, и мы объявляем/определяем их с исключением синтаксиса, который определяет язык.
"Это похоже на академическое различие, действительно ли это на практике?"
Может быть, может и нет. Мое мнение заключается в том, что интерпретация синтаксиса, подобная приведенному выше, как вызов чистого конструктора, рисует неверную картину того, что такое конструктор. Конструктор инициализирует объект; он не выделяет эту память объекта, возвращает инициализированный объект, связывает символ с этим объектом или что-то еще, что делается с помощью определений переменных и преобразований типов. Кроме того, он может создавать путаницу, подобную той, что существует в OP, который ожидал единообразного синтаксиса, потому что он думал, что эти две конструкции являются вызовами конструктора.
Зачем использовать неточный synecdoche, когда у нас есть формальные термины, которые позволяют избежать путаницы?