我对功能模板参数类型扣除程序有疑问。
举个例子:
#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>
,因此可以应用统一初始化语义。我不知道的是类型推导发生的顺序。我真的很感激这方面的一些信息。
提前致谢!
答案 0 :(得分:9)
让我们使用一个更简单的例子:
template<class T>
void foo(T, T);
foo(42, {});
函数调用有两个参数:
int
(整数字面){}
后者{}
可以是表达式列表的一部分,但它不是表达式本身。 表达式列表定义为初始化列表。 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
和{}
调用此函数。后者将执行复制列表初始化,从而导致第二个参数的值初始化。