У меня есть вопрос, касающийся процедуры вывода типа параметра шаблона функции.
Возьмите этот пример:
#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>
и так и так может быть применена единая семантика инициализации. Я понятия не имею, в каком порядке происходит вывод типа. Я был бы очень признателен за информацию об этом.
Заранее спасибо!
Давайте использовать еще более простой пример:
template<class T>
void foo(T, T);
foo(42, {});
Вызов функции имеет два аргумента:
int
(целочисленный литерал){}
Последний, {}
может быть частью список_выражений но это не выражение сам. список_выражений определяется как инициализатора-лист. рамно-Init-листы не имеют типа.
Вывод типа шаблона выполняется для каждого параметра функции индивидуально [temp.deduct.type] / 2. [temp.deduct.call] / 1 сообщает о выводе типа для параметра функции P
:
Если удалить ссылки и cv-квалификаторы из
P
дает
std::initializer_list<
П’>
для некоторых П’ и аргумент
список инициализатора, затем вместо каждого элемента выполняется удержание
из списка инициализатора, принимая П’ как параметр шаблона функции
тип и элемент инициализатора в качестве аргумента. В противном случае,
Аргумент списка инициализатора заставляет параметр считаться
не выводимый контекст. [акцент мой]
Так в звонилке foo(42, {});
T
не будет выведен из второго аргумента {}
, Тем не мение, T
можно вывести из первого аргумента.
В общем, мы можем вывести T
из нескольких параметров функции. В этом случае выведенные типы должны точно соответствовать [temp.deduct.type] / 2. Нет проблем, если тип выводится только из одного параметра функции, но используется где-то еще (в другом параметре функции, который находится в невыгруженном контексте, в типе возврата и т. Д.). Вычисление типа может потерпеть неудачу, например когда параметр шаблона не может быть выведен из любой параметр функции и не задан явно.
После вычета T
будет заменен int
производя сигнатуру функции, похожую на:
void foo<int>(int, int);
Эта функция может быть вызвана с двумя аргументами 42
а также {}
, Последний будет выполнять инициализацию копирования списка, что приведет к инициализации значения второго параметра.