GCC в соответствии с C ++ 11 не может определить тип для первых двух вызовов bar
. Он предупреждает, потому что он реализует расширение до C ++ 11.
Стандарт гласит, что если аргумент функции в вызове шаблона функции равен { ... }
, а параметр не равен initializer_list<X>
(необязательно, ссылочный параметр), то тип параметра не может быть определен с помощью {...}
. Если параметр равен , например initializer_list<X>
, то элементы списка инициализатора выводятся независимо путем сравнения с X
, и каждый из выводов элементов должен совпадать.
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
}
В этом примере с первым все в порядке, а со вторым тоже все в порядке, поскольку первый элемент дает тип int
, а второй элемент сравнивает {2}
с T
- этот вывод не может привести к ограничению, поскольку ничего не выводит, следовательно в конечном итоге второй вызов принимает T
как int
. Третий не может вывести T
ни по какому элементу, следовательно, это НЕ ОК. Последний вызов дает противоречивые выводы для двух элементов.
Один из способов сделать эту работу - использовать такой тип, как тип параметра
template <class T> void bar(std::initializer_list<std::initializer_list<T>> x) {
// ...
}
Я должен отметить, что делать std::initializer_list<U>({...})
опасно - лучше удалите эти (...)
вокруг скобок. В вашем случае это работает случайно, но рассмотрим
std::initializer_list<int> v({1, 2, 3});
// oops, now 'v' contains dangling pointers - the backing data array is dead!
Причина в том, что ({1, 2, 3})
вызывает конструктор копирования / перемещения initializer_list<int>
, передавая ему временный initializer_list<int>
, связанный с {1, 2, 3}
. Этот временный объект будет уничтожен и умрет после завершения инициализации. Когда тот временный объект, который связан со списком, умирает, массив резервных копий, содержащий данные, также будет уничтожен (если перемещение исключено, оно будет жить так же долго, как «v»; это плохо, так как он даже не будет себя вести плохо гарантированно!). Опуская парены, v
напрямую связывается со списком, а данные резервного массива уничтожаются только при уничтожении v
.