模板并不总是猜测初始化列表类型

m42*_*42a 11 c++ templates initializer-list c++11

#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
}
Run Code Online (Sandbox Code Playgroud)

这不能在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的类型而不是第二次调用的类型,除了漫长而丑陋的演员之外,有没有办法解决这个问题呢?

Joh*_*itb 19

根据C++ 11的GCC 不能推断前两次调用的类型bar.它发出警告,因为它实现了对C++ 11 的扩展.

标准说当一个函数模板调用中的函数{ ... }参数是a 而参数不是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
}
Run Code Online (Sandbox Code Playgroud)

在这个例子中,首先是确定,二是也没关系,因为第一要素收益类型int,第二个元素比较{2}反对T-这个扣除不能产生constradiction因为它没有演绎什么,所以最终第二次调用需要Tint.第三个不能T用任何元素推断,因此不行.最后一次调用产生了两个元素的矛盾扣除.

使这项工作的一种方法是使用这种类型作为参数类型

template <class T> void bar(std::initializer_list<std::initializer_list<T>> x) {
  // ...
}
Run Code Online (Sandbox Code Playgroud)

我应该注意,做std::initializer_list<U>({...})是危险的 - 更好地删除(...)括号周围的那些.在你的情况下,它碰巧是偶然工作,但考虑一下

std::initializer_list<int> v({1, 2, 3});
// oops, now 'v' contains dangling pointers - the backing data array is dead!
Run Code Online (Sandbox Code Playgroud)

原因是({1, 2, 3})调用copy/move构造函数initializer_list<int>传递一个临时initializer_list<int>关联的{1, 2, 3}.然后,当初始化完成时,该临时对象将被销毁并死亡.当与列表关联的临时对象死亡时,保存数据的备份数组也将被销毁(如果移动被省略,它将与"v"一样长;这很糟糕,因为它甚至不会表现保证不好!).通过省略parens,v直接与列表相关联,并且后备阵列数据仅在v被销毁时被销毁.


Nic*_*las 5

列表初始化依赖于知道正在初始化的类型.{1}可能意味着许多事情.当应用于一个int,将其与1填充它当应用到std::vector<int>,这意味着以创建一个元素vector,与1在第一个元素.等等.

当您调用模板函数时,其类型完全不受约束,则没有用于列表初始化的类型信息.没有类型信息,列表初始化不起作用.

例如:

bar({{0,1}});
Run Code Online (Sandbox Code Playgroud)

你希望这是类型std::initializer_list<std::pair<int,int>>.但编译器怎么知道呢?bar第一个参数是一个不受约束的模板; 它几乎可以是任何类型.编译器怎么可能猜到你的意思是这个特定类型?

很简单,它不能.编译器很好,但它们不是透视的.列表初始化只能在存在类型信息的情况下工作,而无约束模板将删除所有这些.

根据C++ 11,根据所有权利,该行应该无法编译.它无法推断出您的预期类型{...},因此它应该失败.这看起来像GCC的bug或其他东西.