2

以下代码编译:

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>含义。为什么我仍然可以通过参数指定?这里使用的是什么模板机制?J2...I...IIndices

更新:当前的答案没有解释为什么我可以不推导出一个论点(明确指定)但推断出其他论点。这究竟什么时候起作用?我希望我不依赖未定义的行为。标准是否允许我在上面做的事情?

4

3 回答 3

6

参数 unpack由编译器从函数参数...I推导出来。它被称为模板参数推导

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

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是一个函数模板,但如您所见,我们没有显式传递模板参数。这是因为模板参数是编译器自己从函数参数推导出来的。

希望有帮助。

于 2013-07-11T14:25:10.423 回答
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

于 2013-07-11T20:48:58.560 回答
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)将被推导为模板参数的空序列。如果所有的模板参数都可以推导出来,那么它们都可以被省略;在这种情况下,空模板参数列表 <> 本身也可以省略。在演绎完成但失败的上下文中,或在未完成演绎的上下文中,如果指定了模板参数列表,并且它与任何默认模板参数一起标识单个函数模板特化,则模板 ID 是左值用于功能模板专业化。

于 2013-07-11T20:55:28.127 回答