c – 模板并不总是猜测初始化列表类型
作者:互联网
#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(< brace-enclosed initializer list>)’.为什么gcc可以推断出第一次调用bar的类型而不是第二次调用的类型,除了漫长而丑陋的演员之外,有没有办法解决这个问题呢?
解决方法:
根据C 11的GCC不能推断前两次调用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
}
在这个例子中,第一个是OK,第二个也是OK,因为第一个元素产生int类型,第二个元素将{2}与T进行比较 – 这个推论不能产生一个constradiction,因为它不会推导出任何东西,因此最终第二个调用将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”一样长;这很糟糕,因为它甚至不会表现保证不好!).通过省略parens,v直接与列表相关联,并且仅在v被销毁时销毁后备阵列数据.
标签:c,c11,templates,initializer-list 来源: https://codeday.me/bug/20190926/1821889.html