Мы знаем, что T v(x);
называется прямой инициализацией, а T v = x;
называется copy-initialization, что означает, что он построит временный T
из x
, который будет скопирован/перенесен в v
( который, скорее всего, уклонился).
Для инициализации списка, стандарт различает две формы, в зависимости от контекста. T v{x};
называется direct-list-initialization, а T v = {x};
называется copy-list-initialization:
§8.5.4 [dcl.init.list] p1
[...] Инициализация списка может возникать при прямой инициализации или инициализации копирования контексты; инициализация списка в контексте прямой инициализации называется инициализацией прямого списка, а инициализация списка в контексте инициализации копирования называется copy-list-initialization. [...]
Однако по всему стандарту есть только две ссылки. Для инициализации прямого списка он упоминается при создании временных файлов, таких как T{x}
(§5.2.3/3
). Для инициализации списка-экземпляра для выражения в выражениях return, таких как return {x};
(§6.6.3/2
).
Теперь, как насчет следующего фрагмента?
#include <initializer_list>
struct X{
X(X const&) = delete; // no copy
X(X&&) = delete; // no move
X(std::initializer_list<int>){} // only list-init from 'int's
};
int main(){
X x = {42};
}
Как правило, из шаблона X x = expr;
мы ожидаем, что код не скомпилируется, потому что конструктор перемещения x
определяется как delete
d. Тем не менее, последние версии Clang и GCC компилируют приведенный выше код очень хорошо, и после копания бит (и нахождения вышеуказанной цитаты) это кажется правильным поведением. Стандарт только когда-либо определяет поведение для всей инициализации списка и не проводит различий между этими двумя формами вообще, за исключением вышеупомянутых пунктов. Ну, по крайней мере, насколько я вижу, в любом случае.
Итак, чтобы подытожить мой вопрос еще раз:
Какая польза от разбиения списка-инициализации на две формы, если они (по-видимому) делают то же самое?