#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中编译,它会对标记的行deducing ‘T’ as ‘std::initializer_list<std::initializer_list<int> >’
发出警告,并为标记的行no matching function for call to ‘bar(<brace-enclosed initializer list>)’
发出错误。为什么gcc可以推断出第一次调用bar的类型而不是第二次调用的类型,除了漫长而丑陋的演员之外,有没有办法解决这个问题呢?
答案 0 :(得分:19)
根据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
进行比较 - 这个推论不能产生一个常量,因为它没有推导出任何东西,因此最终第二次调用将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”一样长;这很糟糕,因为它甚至不会表现保证不好!)。通过省略parens,{{1}}与列表直接关联,并且仅在{{1}}被销毁时销毁支持数组数据。
答案 1 :(得分:5)
列表初始化依赖于知道正在初始化的类型。 {1}
可能意味着许多事情。应用于int
后,它会填充1.当应用于std::vector<int>
时,表示创建一个元素vector
,第一个元素1
元件。等等。
当您调用模板函数时,其类型完全不受约束,则没有用于列表初始化的类型信息。如果没有类型信息,列表初始化将无法正常工作。
例如:
bar({{0,1}});
您希望它属于std::initializer_list<std::pair<int,int>>
类型。但编译器怎么知道呢? bar
的第一个参数是无约束模板;它可以是任何类型。编译器怎么可能猜到你的意思是这个特定类型?
很简单,它不能。编译器很好,但它们不是透视的。列表初始化只能在存在类型信息的情况下工作,而无约束模板会删除所有这些。
根据C ++ 11,根据所有权利,该行应该无法编译。它无法推断出您希望{...}
的类型,因此它应该失败。这看起来像GCC的bug或其他什么。