std :: initializer_list, согласованная инициализация и заголовок

Читая о другой теме, я встретил странное поведение, по крайней мере, для меня. Вся эта мысль возникла из специальных взаимодействий между 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 должен быть в стандартных объектах более или менее таким же образом, как lambdas являются вызываемыми объектами (в строгом смысле, то есть объектом с operator()). Другими словами, неопределенные согласованные определения должны по умолчанию иметь значение std::initializer_list же как lambdas (в основном) неназванные вызываемые объекты.

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

UPDATE: заголовок для initializer_list оказался включенным транзитивно из iostream (действительно странно). Однако остается вопрос: почему вызов работает для std::initializer_list а не для std::vector?

Ответ 1

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

... Шаблон std :: initializer_list не предопределен; если заголовок <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])...

Ответ 2

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

[Temp.deduct.type]/р5

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

...

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

ЧФР. cppreference ex.6

Ответ 3

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

Реализация GCC <vector> включает <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