功能模板参数包不在参数列表的末尾

时间:2016-07-28 07:21:33

标签: c++ templates c++11 variadic-templates variadic-functions

以下代码编译并运行正常。

void foo() {

}

template <typename T, typename... Args>
void foo(T x, Args... args) {
  cout << x << endl;
  foo(args...);
}

// inside main()
foo(1,1,1);

此其他代码无法编译:

void foo() {

}

template <typename... Args, typename T>
void foo(Args... args, T x) {
  foo(args...);
  cout << x << endl;
}

// inside main()
foo(1,1,1);

编译器说调用foo(1,1,1)没有匹配函数,并说foo(Args... args, T x)是候选函数,但模板参数推导/替换失败,因为候选者需要1个参数,但提供了3个参数

这种情况是否存在任何编译器无法处理的歧义?这个编译错误对我来说似乎不合逻辑。也许这有点与C ++标准无关?

2 个答案:

答案 0 :(得分:6)

来自Clang's error message的有趣部分是:

main.cpp:11:6: note: candidate template ignored: couldn't infer template argument 'T'

void foo(Args... args, T x) {
     ^

问题是参数包Args...出现之前T

Args...&#34;贪婪&#34;所以没有任何参数可供编译器推导T,因此它失败了。

引用标准(强调我的):

<强> [temp.param] / 11

  

不得遵循功能模板的模板参数包   另一个模板参数除非该模板参数可以   从函数模板的参数类型列表中推导出或具有   默认参数。 [实施例:

...
// U can be neither deduced from the parameter-type-list nor specified
template<class... T, class... U> void f() { } // error
template<class... T, class U> void g() { } // error
     

- 结束示例]

答案 1 :(得分:6)

(此答案基于@JohannesSchaub-litb's comments

根据标准,如果模板参数包用于不在参数列表末尾的函数参数包中,则不能推导出模板参数包。

§14.8.2.1/1 Deducing template arguments from a function call [temp.deduct.call]

  

当函数参数包出现在非推导的上下文中时   ([temp.deduct.type]),该参数包的类型永远不会   推导。 [例如:

template<class T1, class ... Types> void g1(Types ..., T1);

void h(int x, float& y) {
  const int z = x;
  g1(x, y, z);                 // error: Types is not deduced
  g1<int, int, int>(x, y, z);  // OK, no deduction occurs
}
     

- 结束示例]

关于未推断的背景,§14.8.2.5/5 Deducing template arguments from a type [temp.deduct.type]

  

不在参数声明列表末尾出现的函数参数包。

因此foo(1,1,1);失败的直接原因是不推导出模板参数Args,这对于使函数调用有效是必要的。

为了解释错误消息,未推导出的模板参数包将被推导为模板参数 [1] 的空序列,这意味着它将被省略。然后foo(1,1,1);失败,因为参数的数量不匹配,这就是编译器所抱怨的。

正如所示的标准示例一样,您可以明确指定模板参数以避免类型推导,即使它不符合代码的原始意图。如:

template <typename T, typename... Args>
void foo(Args... args, T x) {
}

int main() {
    // inside main()
    foo<int, int, int>(1, 1, 1);
}

Here还有一些额外的信息。

[1]我在标准中找不到关于此的直接表达。最接近的是this,“否则推导出的尾随模板参数包([temp.variadic])将被推导为空的模板参数序列。”