Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forwarding initializer list expressions

Initializer list expressions are really convenient for initializing C++ containers:

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

...but it seems that a brace-enclosed initializer list expression, like {1,2,3} will only bind to a function that takes a std::initializer_list<int> - it doesn't seem to bind to a universal (forwarding) reference:

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

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

This outputs:

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’

(This was the result with GCC 4.7.2.)

This unfortunately means we can't forward an initializer list expression. Since it would be very convenient to do that, I'd like to ask why is it that this doesn't work? Why can't a brace enclosed initializer list expression bind to a forwarding reference? Or is this allowed, and perhaps my compiler is just too old?

like image 517
Siler Avatar asked Feb 06 '15 17:02

Siler


2 Answers

It's not that it can't bind to the parameter of your function; it's just that the compiler is unable to detect the type of your template. This compiles:

#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});
}
like image 119
Drax Avatar answered Oct 02 '22 16:10

Drax


The initializer list cannot be deduced in this case. This is actually covered explicitly by the standard in [temp.deduct.call]:

Template argument deduction is done by comparing each function template parameter type (call it P) with the type of the corresponding argument of the call (call it A) as described below. If P is a dependent type, [...]. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context (14.8.2.5). [ Example:

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

The example here for g is exactly your case - T is not a dependent type, so this is considered to be a non-deduced context. The compiler is correct to reject your code.

like image 36
Barry Avatar answered Oct 02 '22 15:10

Barry