Списки инициализации в фигурных скобках и порядок вывода типа шаблона функции

У меня есть вопрос относительно процедуры вывода типа параметра шаблона функции.

Возьмите этот пример:

#include <vector>
#include <sstream>
#include <string>
#include <iterator>
#include <fstream>

int main()
{
    std::ifstream file("path/to/file");
    std::vector<int> vec(std::istream_iterator<int>{file},{}); // <- This part
    return 0;
}

Если я правильно понимаю, второй параметр выводится как тип std::istream_iterator, из которого вызывается конструктор по умолчанию.

Соответствующий конструктор std::vector объявляется как:

template <class InputIterator>
         vector (InputIterator first, InputIterator last,
                 const allocator_type& alloc = allocator_type());

Поскольку первый тип параметра выводится как std::istream_iterator<int>, второй параметр также выводится как std::istream_iterator<int>, поэтому может применяться единая семантика инициализации. Чего я не знаю, так это того, в каком порядке происходит вывод типа. Я был бы очень признателен за некоторую информацию об этом.

Заранее спасибо!


person Veritas    schedule 05.06.2014    source источник
comment
Это работает? Если это так, первое должно быть вычтено в первую очередь.   -  person Dani    schedule 05.06.2014


Ответы (1)


Возьмем еще более простой пример:

template<class T>
void foo(T, T);

foo(42, {});

Вызов функции имеет два аргумента:

  • выражение prvalue типа int (целочисленный литерал)
  • список инициализации в фигурных скобках {}

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

Вычет типа шаблона выполняется для каждого параметра функции отдельно [temp.deduct.type]/2. [temp.deduct.call]/1 указывает на вывод типа для параметра функции P:

Если удаление ссылок и cv-квалификаторов из P дает std::initializer_list<P'> для некоторого P', а аргумент является списком инициализаторов, то вместо этого выполняется вычитание для каждого элемента список инициализаторов, принимая P' в качестве типа параметра шаблона функции и элемент инициализатора в качестве аргумента. В противном случае аргумент списка инициализатора приводит к тому, что параметр считается невыведенным контекстом. [выделено мной]

Таким образом, в вызове foo(42, {}); T не будет выводиться из второго аргумента {}. Однако T можно вывести из первого аргумента.

В общем, мы можем вывести T из нескольких параметров функции. В этом случае выведенные типы должны точно соответствовать [temp.deduct.type]/2. Нет проблем, если тип выводится только из одного параметра функции, но используется в другом месте (в другом параметре функции, который находится в невыведенном контексте, в возвращаемом типе и т. д.). Вывод типа может потерпеть неудачу, например. когда параметр шаблона не может быть выведен из любого параметра функции и не задан явно.

После вычета T будет заменено на int, создавая сигнатуру функции, подобную:

void foo<int>(int, int);

Эту функцию можно вызвать с двумя аргументами 42 и {}. Последний выполнит инициализацию списка копирования, ведущую к инициализации значения второго параметра.

person dyp    schedule 05.06.2014
comment
Примерно так я это и видел интуитивно. Спасибо, что прояснили ситуацию. - person Veritas; 05.06.2014