Ответ 1
Это основная проблема 1343 "Последовательность инициализации без класса" , которая была принята в качестве отчета о дефектах в ноябре 2016 года на бумаге P0570R0. Предлагаемая резолюция является частью С++ 17, но не является частью С++ 14, поэтому (если комитет не решит опубликовать исправление к С++ 14), это точка различия между С++ 17 и C + +14.
С++ 14
Правильный вывод в соответствии с правилами стандарта С++ 14 составляет 1, 1
для массива и 1, 2
для вектора; это связано с тем, что для построения вектора (в том числе из списка с привязкой-инициализацией) требуется вызов конструктора при построении массива.
Язык, который регулирует это, находится в [intro.execution]:
10 - Полное выражение - это выражение, которое не является подвыражением другого выражения. [...] Если языковая конструкция определена для получения неявного вызова функции, использование конструкции языка считается выражением для целей этого определения. [...]
Это хорошо, как обзор верхнего уровня, но он оставляет неотвеченные некоторые вопросы:
- Именно эта конструкция языка считается конструкцией, производящей неявный вызов функции;
- Что на самом деле считается неявным вызовом функции; предположительно вызов определенного пользователем конструктора является вызовом функции, но как насчет конструктора, который по умолчанию установлен или определен как дефолт?
Массив - это совокупность, поэтому инициализируется из списка с привязкой-init-init в соответствии с [dcl.init.aggr]; это говорит о том, что каждый элемент инициализируется непосредственно из соответствующего элемента списка, поэтому не существует неявного вызова функции (по крайней мере, не соответствует общей инициализации). На уровне синтаксиса в инициализаторе ( [dcl.init]/1), используя список с привязкой-init-списком в качестве элемента для выравнивания или равносильности, полные выражения являются выражениями, содержащимися в скобки и разделенные запятыми. В конце каждого полного выражения деструкторы временных рядов должны выполняться, так как здесь не существует ни одного из трех контекстов, упомянутых в [class.temporary].
Случай для инициализации вектора отличается, поскольку вы используете конструктор initializer_list
, поэтому возникает неявный вызов функции (т.е. конструктор initializer_list
); это означает, что существует неявное полное выражение, окружающее всю инициализацию, поэтому временные файлы уничтожаются только тогда, когда завершается инициализация вектора.
Смутно, [dcl.init.list] говорит, что ваш код "примерно эквивалентен":
const int __a[2] = {int{ID().id}, int{ID().id}}; // #1
std::vector<int> vec(std::initializer_list<int>(__a, __a + 2));
Однако это нужно читать в контексте - например, массив, поддерживающий initializer_list
, имеет время жизни, ограниченное инициализацией вектора.
Это было намного яснее в С++ 03, который был в [intro.execution]:
13 - [Примечание: некоторые контексты в С++ вызывают оценку полного выражения, которое получается из синтаксической конструкции кроме выражения (5.18). Например, в 8.5 один синтаксис для инициализатора
( expression-list )
но результирующая конструкция представляет собой вызов функции функции-конструктора с выражением-списком в качестве аргумента список; такой вызов функции является полным выражением. Например, в 8.5 другой синтаксис для инициализатора= initializer-clause
но снова полученная конструкция может быть вызовом функции для функции-конструктора с одним присваиванием-выражением как аргумент; снова вызов функции является полным выражением. ]
Этот абзац полностью поражен С++ 11; это соответствовало разрешению CWG 392. В результате путаница, по-видимому, не была предназначена.
С++ 17
После P0570R0 [intro.execution] заявляет, что полное выражение: [...]
- init-declarator ([dcl.decl]) [...], включая составляющие выражения инициализатора, или [...]
- выражение, которое не является подвыражением другого выражения и которое не является частью полного выражения.
Итак, в С++ 17 полное выражение arr[]{ID().id, ID().id}
и vec{ID().id, ID().id}
соответственно, а правильный вывод 1, 2
в каждом случае, так как уничтожение первого временного ID
откладывается до конец полного выражения.