模板不总是猜测初始化程序列表类型 [英] Templates don't always guess initializer list types
问题描述
#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
}
这不会在gcc 4.5.3中编译,给出了标记行推断'T'为'std :: initializer_list< std :: initializer_list< int> <>'
和标记行没有匹配函数调用'bar(<括号括起初化程序列表>)'的错误
。为什么gcc可以推导出第一次调用bar的类型,但不是第二个调用,除了长期和丑陋的转换之外,有没有办法解决这个问题?
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?
推荐答案
GCC根据C ++ 11 无法推导出前两个调用 bar
的类型。它警告,因为它实现了一个扩展到C ++ 11。
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.
标准说当调用函数模板的函数参数是 {...}
并且参数不是 initializer_list< X>
(可选地是参考参数),那么参数的类型不能由 {...}
。如果参数是 initializer_list
,则通过与比较独立地推断初始化器列表的元素, X
,每个元素的扣除必须匹配。
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
}
在此示例中,第一个是OK,第二个是也是因为第一个元素产生类型 int
,第二个元素比较 {2}
与 T
- 这个推导不能产生约束,因为它不推导任何东西,因此最终第二个调用 T
为 int
。第三个不能通过任何元素推导出 T
,因此是不行的。最后一个调用产生两个元素的矛盾的扣除。
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) {
// ...
}
我应该注意,执行 std :: initializer_list< U>({...})
那些(...)
在大括号。在你的情况下,它偶然是意外工作,但考虑
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!
原因是({1,2,3})
调用
initializer_list< int>
的copy / move构造函数传递临时 initializer_list< int>
与 {1,2,3}
相关联。该临时对象将被破坏,并在初始化完成时死亡。当与该列表相关联的临时对象死亡时,保存数据的备份数组也将被销毁(如果移动被取消,它将只存在v;这是不好的,因为它甚至不会表现坏的保证!)。通过省略括号, v
直接与列表相关联,并且仅当 v
为
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.
这篇关于模板不总是猜测初始化程序列表类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!