Пересылка выражений в списке инициализаторов

Инициализационные списки списков действительно удобны для инициализации контейнеров С++:

std::vector<int>({1, 2, 3})

... но кажется, что выражение списка инициализаторов, заключенное в фигурной скобке, подобно {1,2,3}, будет привязываться только к функции, которая принимает std::initializer_list<int> - она, похоже, не привязана к универсальной (пересылающей) ссылке:

template <class T>
void foo(T&& v)
{
  std::vector<int>(std::forward<T>(v));
}

int main()
{
  foo({1, 2, 3})
}

Выводится:

test2.cpp:11:6: note: template<class U> void foo(U&&)
test2.cpp:11:6: note:   template argument deduction/substitution failed:
test2.cpp:33:13: note:   couldn't deduce template parameter ‘U’

(Это был результат с GCC 4.7.2.)

К сожалению, мы не можем перенаправить выражение списка инициализаторов. Поскольку было бы очень удобно это сделать, я хотел бы спросить, почему это не работает? Почему не удается привязать выражение списка инициализаторов в скобках к ссылке пересылки? Или это разрешено, и, возможно, мой компилятор слишком стар?

Ответ 1

Не то, чтобы он не мог привязываться к параметру вашей функции; это просто, что компилятор не может определить тип вашего шаблона. Это компилируется:

#include <vector>

template <class T>
void foo(T&& v)
{
  std::vector<int>(std::forward<T>(v));
}

int main()
{
  foo(std::initializer_list<int>{1, 2, 3});
}

Ответ 2

В этом случае список инициализаторов не может быть выведен. Это явно рассматривается стандартом в [temp.deduct.call]:

Вывод аргумента шаблона производится путем сравнения каждого параметра параметра шаблона функции (назовем его P) с помощью тип соответствующего аргумента вызова (назовите его A), как описано ниже. Если P является зависимым типом, [...]. В противном случае аргумент списка инициализатора приводит к тому, что параметр считается не выведенным контекст (14.8.2.5). [Пример:

template<class T> void f(std::initializer_list<T>);
f({1,2,3}); // T deduced to int
f({1,"asdf"}); // error: T deduced to both int and const char*

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

Пример здесь для g - это именно ваш случай - T не является зависимым типом, поэтому это считается не выведенным контекстом. Компилятор правильно отклоняет ваш код.