为什么这个函数模板调用工作?

时间:2013-07-11 14:23:43

标签: c++ templates c++11

以下代码编译:

template<int...>
struct Indices {};

template<int J, int ...I>
void foo(Indices<I...>) {}

int main(int argc, char **argv)
{
  foo<2>(Indices<3,4,5>()); //why does this work?
  return 0;
}

在函数调用中,我认为J参数变为2...I参数变为3,4,5

但为什么这样呢?我只在2指定了foo<2>,这意味着我将J指定为2...I指定为空。为什么我仍然可以通过...I参数指定Indices?这里使用了什么模板机制?

更新:目前的答案并不能解释为什么我可以推断出一个不推断(明确指定)的参数,而推断其他参数。什么时候这确实有效?我希望我不依赖于未定义的行为。标准是否允许我正在做的事情?

3 个答案:

答案 0 :(得分:6)

参数unpack ...I由编译器从函数参数中推导 。它被称为template argument deduction

以下是一些简单但有用的示例:

template<typename T> 
void f(T const&) {}

f(10);   //T is deduced as int
f(10.0); //T is deduced as double
f("10"); //T is deduced as char[3]

标准库中的许多函数都是函数模板,通常会推导出模板参数。这是一个例子:

std::vector<int> vi;
std::vector<std::string> vs;
//...
std::sort(vi.begin(), vi.end()); //template argument deduction
std::sort(vs.begin(), vs.end()); //template argument deduction

这里std::sort是一个函数模板,但正如您所看到的,我们没有显式传递模板参数。这是因为模板参数是由编译器本身从函数参数推断出来的。

希望有所帮助。

答案 1 :(得分:2)

要添加到nawaz答案:必须提供无法推断的模板参数,并且提供的模板参数必须按照定义的顺序排列。这意味着如果模板参数可能需要提供,最好将其放在模板参数列表中的第一位。例如

template<typename A, typename B> A foo(B);
template<typename B, typename A> A bar(B);

auto x = foo<int>(0.0);       // A=int, B=double;
auto y = foo<int,double>(0);  // A=int, B=double, argument implicitly cast to double
auto z = bar<int>(0);         // error: cannot deduce A
auto w = bar<int,double>(0);  // A=double, B=int;

在这两种情况下都可以推导B(来自函数参数类型),但A不能。所以foo更方便,因为只能提供一个模板参数。对于bar,第一个模板参数是可推导的,但不是第二个。因此,必须提供两者。 (只是为了澄清转换autodoubleint对于手头的问题没有任何区别。)

答案 2 :(得分:2)

如果可以在编译时推断出其他参数,则允许仅指定函数调用的参数的一部分(第一个)。例如:

template<typename Ret, typename Arg>
Ret cast(Arg x){
    return x;
}

cast<double>(5);

实际上你甚至可以编译这段代码:

template<int...>
struct Indices {};

template<int J, int ...I>
void foo(Indices<I...>) {}

int main(int argc, char **argv)
{
  foo<2,3>(Indices<3,4,5>()); //ok 2,3,4,5 starts with 2,3
  return 0;
}

但不是这一个:

template<int...>
struct Indices {};

template<int J, int ...I>
void foo(Indices<I...>) {}

int main(int argc, char **argv)
{
  foo<2,1>(Indices<3,4,5>()); //no way to make x,3,4,5 start with 2,1
  return 0;
}

参见C ++ 11标准(N3242草案)第3部分第14.1.8节。

  

可以推导(14.8.2)或获得的尾随模板参数   来自默认模板 - 参数可以从列表中省略   显式模板参数。尾随模板参数包   (14.5.3)未以其他方式推导出的将被推断为空序列   模板参数。如果可以推导出所有模板参数,   它们都可以省略;在这种情况下,空模板参数   列表&lt;&gt;本身也可以省略。在演绎的情况下   完成和失败,或者在没有进行演绎的情况下,如果a   模板参数列表是指定的,它与任何默认值一起   模板参数,标识单个函数模板   特化,然后template-id是函数的左值   模板专业化。