std :: initializer_list, фигурная инициализация и заголовок - PullRequest
0 голосов
/ 22 октября 2018

Читая о другой теме, я столкнулся со странным поведением, по крайней мере, со мной.Вся эта мысль возникла из особых взаимодействий между auto и фигурными скобками.Если вы напишите что-то вроде:

auto A = { 1, 2, 3 }

, компилятор выведет A в std::initializer_list.Странно то, что подобное правило применяется не только к auto, где для этого могут быть особые причины, но и к другим вещам.Если вы напишите следующее:

template<typename T>
void f(std::vector<T> Vector)
{
    // do something
}

, вы, конечно, не сможете так его назвать:

f({ 1, 2, 3});

, даже если std::vector можно инициализировать в скобках.Однако, если вы замените std::vector на std::initializer_list, вызов сработает, и компилятор правильно выведет int как тип T.Однако более интересным является то, что в первом случае вам нужно #include <vector>, во втором - #include <initializer_list>.Это заставило меня задуматься, и после теста я понял, что std::initializer_list не нужен собственный заголовок, так что это в некоторой степени часть "базовых" функций.

Более того, чтобы все имело смысл,std::initializer_list должно относиться к стандартным объектам более или менее так же, как лямбды к вызываемым объектам (в самом строгом смысле, это объект с operator()).Другими словами, безымянные фигурные скобки по умолчанию должны иметь значение std::initializer_list, так же как лямбды (в основном) являются неназванными вызываемыми объектами.

Правильно ли это рассуждение?Более того, можно ли изменить это поведение и, если да, то как?

ОБНОВЛЕНИЕ: было обнаружено, что заголовок для initializer_list транзитивно включен из iostream (действительно странно).Однако остается вопрос: почему звонок работает на std::initializer_list, а не на std::vector?

Ответы [ 3 ]

0 голосов
/ 22 октября 2018

Онлайн-ссылка CPP для <vector> показывает, что <initializer_list> включено в его заголовок.

Реализация <vector> в GCC включает <initializer_list>.Это, вероятно, верно и для других реализаций.По этой причине вам не нужно было включать <initializer_list> отдельно.

Проверьте этот источник для GCC 4.6.2, который включает <initializer_list> в заголовке <vector>.https://gcc.gnu.org/onlinedocs/gcc-4.6.2/libstdc++/api/a01069_source.html

0 голосов
/ 22 октября 2018

Вы, вероятно, включаете заголовок транзитивно из <vector> или <iostream>, имейте в виду, что стандарт явно применяет невыбранный контекст для случая std::vector

[temp.deduct.type] / p5

Неведуемые контексты:

...

  • Функциональный параметр, для которогосвязанный аргумент - это список инициализаторов ([dcl.init.list]), но у параметра нет типа, для которого указывается вычет из списка инициализаторов ([temp.deduct.call]).

Ср. cppreference ex.6

0 голосов
/ 22 октября 2018

Плохо сформирован (, поэтому требуется диагностика ), чтобы не включать заголовок initializer_list, если мы используем std::initializer_list.Мы можем видеть это из [dcl.init.list] p2 :

... Шаблон std :: initializer_list не предопределен;если заголовокне включается до использования std :: initializer_list - даже неявного использования, в котором тип не назван (9.1.7.4) - программа некорректна.

Скорее всего, вывключая транзитивный заголовок, который правильно сформирован, но делает ваш код более хрупким, поэтому включите то, что вы используете.

Мы можем видеть из примера с живым крестиком, что при отсутствии включений мы получаем диагностику в соответствии с требованием gcc / clang / MSVC например:

error: use of undeclared identifier 'std'    
void foo( std::initializer_list<int>) {
          ^

и включая <vector> или <iostream> мы больше не получаем диагностику .

Почемуон не выводит, как вы ожидаете, охватывается [temp.deduct.type] p5 , который говорит нам, что это не выводимый контекст:

Неведуемые контексты:
...
- Параметр функции, для которого связанный аргумент является списком инициализатора ([dcl.init.list]), но у параметра нет типа, для которого указывается вычет из списка инициализатора ([temp.deduct.call]).> [Пример:

template<class T> void g(T);
g({1,2,3});                 // error: no argument deduced for T

- конечный пример]
...

также см. [temp.deduct.call] p1 :

... В противном случае аргумент списка инициализатора приводит к тому, что параметр считается недедуцированным контекстом ([temp.deduct.type]) ...

...