Вы не должны ограничивать узость при использовании инициализации ().
Основная цель этой функции - разрешить использование агрегатов в сценариях пересылки, таких как container::emplace
, конструкторы на месте и т. П. В настоящее время они не работают, потому что std::allocator_traits<>::construct
не будет и не сможет использовать синтаксис инициализации списка, поскольку существует слишком много случаев, когда он скрывает конструкторы, которые вы, возможно, захотите вызвать.
В сценариях переадресации способность точно исключать сужающие преобразования ограничена при работе с агрегатами. Зачем? Учтите это:
struct Agg { int i; };
Agg a{5.0f};
Это не сужающее преобразование, потому что это конкретное литеральное значение с плавающей точкой может быть преобразовано в int
без потери точности. Однако, когда вы пересылаете конструкцию через emplace
и т.п., компилятор не может видеть фактическое значение параметра. Он видит только типы. Так что, если вы должны были сделать:
vector<Agg> v;
v.emplace_back(5.0f);
Все, что видит компилятор, это то, что emplace_back
будет пытаться передать float
в совокупную инициализацию Agg
. Это всегда сужающееся обращение и поэтому всегда незаконно.
Предотвращение сужения инициализации списка имеет некоторый смысл, поскольку braced-init-lists лучше всего использовать локально. Инициализируемый тип известен, и любые литеральные значения будут предоставлены непосредственно там, где используется {}
. Таким образом, имеется достаточно информации для разумного решения сужающихся вопросов.
Как только вы попадаете в переадресацию, это просто не работает. Сужение предотвращает отбраковку параметров, локальные значения которых были бы хорошими.
Итак, вопрос в следующем: хотите ли вы, чтобы emplace(X, Y, Z)
работал так же хорошо, как и Agg{X, Y, Z}
, для всех действительных X, Y и Z? Если ответ «да», то ()
агрегатная инициализация не может предотвратить сужение.