Почему стандарт различает прямую инициализацию списка и инициализацию копирования списка?

Мы знаем это T v(x); называется прямая инициализация, в то время как T v = x; называется копия инициализация, Это означает, что он будет строить временный T от x который будет скопирован / перемещен в v (который, скорее всего, исключен).

Для инициализации списка стандарт различает две формы в зависимости от контекста. T v{x}; называется прямой список инициализация в то время как T v = {x}; называется копирование списка инициализация:

§8.5.4 [dcl.init.list] p1

[…] Инициализация списка может происходить при прямой инициализации или при инициализации копирования
контексты; инициализация списка в контексте прямой инициализации называется прямой список инициализация и инициализация списка в контексте инициализации копирования называется копирование списка инициализация. […]

Однако в целом стандарте есть только две ссылки. Для прямой инициализации списка это упоминается при создании временных T{x} (§5.2.3/3). Для копирования списка инициализации, это для выражения в операторах возврата, таких как 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д. Тем не менее, последние версии Clang и GCC прекрасно компилируют приведенный выше код, и после того, как он немного покопался (и обнаружил приведенную выше цитату), это кажется правильным поведением. Стандарт только когда-либо определяет поведение для всей инициализации списка, и вообще не проводит различия между двумя формами, за исключением вышеупомянутых пунктов. Ну, по крайней мере, насколько я вижу, в любом случае.

Итак, чтобы подвести итог моего вопроса еще раз:

Какая польза от разделения инициализации списка на две его формы, если они (по-видимому) делают одно и то же?

33

Решение

Потому что они не сделать то же самое. Как указано в 13.3.1.7 [over.match.list]:

В случае инициализации copy-list, если выбран явный конструктор, инициализация является некорректной.

Короче говоря, вы можете использовать неявное преобразование только в контексте инициализации копирования списка.

Это было явно добавлено, чтобы сделать равномерную инициализацию не единой. Да, я знаю, как это звучит глупо, но терпите меня.

В 2008, N2640 был опубликован (PDF), взглянуть на текущее состояние равномерной инициализации. Посмотрел конкретно на разницу между прямой инициализацией (T{...}) и копия-инициализация (T = {...}).

Подводя итог, проблема была в том, что explicit конструкторы фактически станут бессмысленными. Если у меня есть какой-то тип T что я хочу иметь возможность быть построенным из целого числа, но я не хочу неявного преобразования, я помечаю конструктор как явный.

Тогда кто-то делает это:

T func()
{
return {1};
}

Без текущей формулировки, это будет называть мой explicit конструктор. Так что хорошего в том, чтобы сделать конструктор explicit если это не сильно изменится?

С текущей формулировкой вам нужно как минимум использовать имя напрямую:

T func()
{
return T{1};
}
29

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector