2013-07-11 55 views
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

但是,它为什么这样工作?我只指定2foo<2>这意味着我指定J2...I为无。为什么我仍然可以通过Indices参数指定...I?这里使用了什么模板机制?

更新:目前的答案并不能解释为什么我可以有一个参数没有推导(明确指定),但推导出其他参数。这个工作到底是什么时候?我希望我不依赖于未定义的行为。标准允许我在上面做什么?

+0

当你说'富<2>(指数<3,4,5>)',你输入一个类型'指数<3,4,5>'入模板函数。由于输入类型在编译时已知,并且该类型对应于第二个模板参数,因此编译器可以匹配'<3,4,5>'和''并生成正确的函数。 – Suedocode

回答

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; 
} 

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

可以推断(14.8.2)或者从默认模板参数获得 尾随模板参数可以从 明确的模板参数列表被省略。未被推导出的尾随模板参数包 (14.5.3)将被推断为模板参数的空序列 。如果可以推导出所有模板参数,则可以全部省略它们;在这种情况下,空模板参数<>本身也可以被省略。在扣除是 完成和失败的情况下,或者在没有完成扣除的上下文中,如果指定了 模板参数列表,并且它与任何默认的 模板参数一起标识单个功能模板 专业化,则模板 - id是功能 模板专用化的左值。

6

参数解包...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是一个函数模板,但你可以看到,我们没有明确地传递模板参数。这是因为模板参数是由编译器本身从函数参数中推导出来的。

希望有所帮助。

+0

我不知道我可以推导出一些参数,还有其他推理参数。你能否详细说明一下? –

+2

@ roger.james:好吧,现在你做了。使用函数模板,您可以明确指定以第一个开始的参数并随时停止。编译器会尝试推导出其余部分。 – AnT

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使得w.r.t.手头上的问题没有区别。)

相关问题