模板不总是猜测初始化程序列表类型 [英] Templates don't always guess initializer list types

查看:122
本文介绍了模板不总是猜测初始化程序列表类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

#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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆