如果函数模板参数出现在函数参数列表中,则不需要指定模板参数。例如,
template<typename T>
void f(const T &t) {}
这里T是一个模板参数,它出现在函数参数列表中,即const T &t。所以调用这个函数时不需要指定模板参数:
f(10); //ok
由于10的类型是int,所以编译器可以从中推导出模板参数T,T变成int。
请注意,由于类型推导是通过使用函数参数的信息完成的,因此称为template argument deduction。现在继续阅读。
如果模板参数没有出现在函数参数列表中,那么您必须提供模板参数。示例:
template<typename T>
void g(const int &i) {}
注意g() 与f() 不同。现在T 不会出现在函数参数列表中。所以:
g(10); //error
g<double>(10); //ok
请注意,如果函数模板也模板化了返回类型,并且返回类型与函数参数列表中出现的类型不同,那么您必须提供返回类型:
template<typename T>
T h(const T &t) {}
由于返回类型T与函数参数相同,所以可以从函数参数中进行类型推导:
h(10); //ok - too obvious now
但如果你有这个:
template<typename R, typename T>
R m(const T &t) {}
那么,
m(10); //error - only T can be deduced, not R
m<int>(10); //ok
请注意,尽管函数模板 m 已模板化为两种类型:R 和 T,但我们在调用它时只提供了一种类型。也就是说,我们写了m<int>(10),而不是m<int,int>(10)。写后者没有坏处,但如果你不这样做,也可以。但有时您必须同时指定两者,即使可以推断出一种类型 T。就是类型参数的顺序不同如下图:
template<typename T, typename R> //note the order : its swapped now!
R n(const T &t) {}
现在,您必须提供两种类型:
n(10); //error - R cannot be deduced!
n<int>(10); //error - R still cannot be deduced, since its the second argument!
n<int,int>(10); //ok
这里的新东西是:类型参数的顺序也很重要。
无论如何,这仅涵盖基本概念。现在我建议您阅读一些关于模板的好书,以了解有关类型推导的所有高级知识。