Templates don't always guess initializer list types
c++, c++11, initializer-list, templates
Solution
GCC according to C++11 cannot deduce the type for either first two calls to `bar`. It warns because it implements an extension to C++11.
The Standard says that when a function argument in a call to a function template is a `{ ... }` and the parameter is not `initializer_list<X>` (optionally a reference parameter), that then the parameter's type cannot be deduced by the `{...}`. If the parameter is such a `initializer_list<X>`, then the elements of the initializer list are deduced independently by comparing against `X`, and each of the deductions of the elements have to match.
template<typename T>
void f(initializer_list<T>);
int main() {
f({1, 2}); // OK
f({1, {2}}); // OK
f({{1}, {2}}); // NOT OK
f({1, 2.0}); // NOT OK
}
In this example, the first is OK, and the second is OK too because the first element yields type `int`, and the second element compares `{2}` against `T` - this deduction cannot yield a constradiction since it doesn't deduce anything, hence eventually the second call takes `T` as `int`. The third cannot deduce `T` by any element, hence is NOT OK. The last call yields contradicting deductions for two elements.
One way to make this work is to use such a type as parameter type
template <class T> void bar(std::initializer_list<std::initializer_list<T>> x) {
// ...
}
I should note that doing `std::initializer_list<U>({...})` is dangerous - better remove those `(...)` around the braces. In your case it happens to work by accident, but consider
std::initializer_list<int> v({1, 2, 3});
// oops, now 'v' contains dangling pointers - the backing data array is dead!
The reason is that `({1, 2, 3})` calls the copy/move constructor of `initializer_list<int>` passing it a temporary `initializer_list<int>` associated with the `{1, 2, 3}`. That temporary object will then be destroyed and die when the initialization is finished. When that temporary object that is associated with the list dies, the backing-up array holding the data will be destroyed too (if the move is elided, it will live as long as "v"; that's bad, since it would not even behave bad guaranteedly!). By omitting the parens, `v` is directly associated with the list, and the backing array data is destroyed only when `v` is destroyed.
Problem
``` #include <initializer_list> #include <utility> void foo(std::initializer_list<std::pair<int,int>>) {} template <class T> void bar(T) {} int main() { foo({{0,1}}); //This works foo({{0,1},{1,2}}); //This works bar({{0,1}}); //This warns bar({{0,1},{1,2}}); //This fails bar(std::initializer_list<std::pair<int,int>>({{0,1},{1,2}})); //This works } ``` This doesn't compile in gcc 4.5.3, it gives a warning for the marked line saying `deducing ‘T’ as ‘std::initializer_list<std::initializer_list<int> >’` and an error for the marked line saying `no matching function for call to ‘bar(<brace-enclosed initializer list>)’`. Why can gcc deduce the type of the first call to bar but not the second, and is there a way to fix this other than long and ugly casting?