Braced-init-lists和函数模板类型推导顺序

时间:2014-06-05 12:03:41

标签: c++ templates c++11 type-deduction list-initialization

我对功能模板参数类型扣除程序有疑问。

举个例子:

#include <vector>
#include <sstream>
#include <string>
#include <iterator>
#include <fstream>

int main()
{
    std::ifstream file("path/to/file");
    std::vector<int> vec(std::istream_iterator<int>{file},{}); // <- This part
    return 0;
}

如果我理解正确,第二个参数推断为std::istream_iterator类型,其默认构造函数被调用。

相应的std::vector构造函数声明为:

template <class InputIterator>
         vector (InputIterator first, InputIterator last,
                 const allocator_type& alloc = allocator_type());

由于第一个参数类型被推导为std::istream_iterator<int>,第二个参数也被推导为std::istream_iterator<int>,因此可以应用统一初始化语义。我不知道的是类型推导发生的顺序。我真的很感激这方面的一些信息。

提前致谢!

1 个答案:

答案 0 :(得分:9)

让我们使用一个更简单的例子:

template<class T>
void foo(T, T);

foo(42, {});

函数调用有两个参数:

  • 类型int(整数字面)
  • 的prvalue表达式
  • braced-init-list {}

后者{}可以是表达式列表的一部分,但它不是表达式本身。 表达式列表定义为初始化列表 braced-init-lists 没有类型。

单独为每个函数参数[temp.deduct.type] / 2完成模板类型推导。 [temp.deduct.call] / 1表示关于函数参数P的类型推导:

  

如果从P中删除引用和cv限定符,则会给出   某些 P'std::initializer_list< P' >,参数为   初始化列表,然后对每个元素执行推导   初始化列表,将 P'作为函数模板参数   type和initializer元素作为其参数。 否则,一个   初始化列表参数使参数被视为a   非推断的背景。 [强调我的]

因此,在通话foo(42, {});中,T不会从第二个参数{}中推断出来。但是,T可以从第一个参数中推断出来。

通常,我们可以从多个函数参数中推导出T。在这种情况下,推导出的类型必须与[temp.deduct.type] / 2完全匹配。如果类型仅从一个函数参数推导出来但在别处使用(在非推导上下文中的另一个函数参数中,在返回类型中等),则没有问题。类型扣除可能会失败,例如当模板参数无法从任何函数参数推导出来且未明确设置时。

扣除后,T将被int替换,产生类似于的函数签名:

void foo<int>(int, int);

可以使用两个参数42{}调用此函数。后者将执行复制列表初始化,从而导致第二个参数的值初始化。