函数模板大致形式如下:
template<typename T>
void f(ParamType parem);
调用形式如下:
f(expr);
在编译期,编译器会通过expr
推导两个类型:一个是T
的类型,另一个是ParamType
的类型;其中T的类型推导结果不仅仅依赖expr
的类型,还依赖ParamType
的形式,而`ParamType分以下几种情况:
ParamType
是个指针或引用,但不是万能引用
template<typename T>
void f(T& param); //param 是个引用
又声明了下列变量:
int x = 27; // x的类型为int
const int cx = x; // cx的类型是const int
const int &rx = x; // rx是x的类型为const int的引用
对param
和T
的类型推导结果为:
f(x); // T的类型为int, param的类型为int&
f(cx); // T的类型为const int, param的类型为const int&
f(rx); // T的类型为const int, param的类型为const int&
在第三个调用中,即使rx
具有引用类型,T
也并未被推导成为一个引用,原因在于,rx的引用性会在类型推导过程中被忽略;
ParamType
是个万能引用(会区分实参是左值还是右值)
template<typename T>
void f(T&& param); //param 是个引用
int x = 27; // x的类型为int
const int cx = x; // cx的类型是const int
const int &rx = x; // rx是x的类型为const int的引用
f(x); // x是左值,所以T的类型为int&, param的类型为int&
f(cx); // cx是左值,所以T的类型为const int&, param的类型为const int&
f(rx); // rx是左值,所以T的类型为const int&, param的类型为const int&
f(27); // 27是右值,所以T的类型为int, param的类型为int&&
ParamType
既非指针也非引用
template<typename T>
void f(T param); //param为按值传递
int x = 27; // x的类型为int
const int cx = x; // cx的类型是const int
const int &rx = x; // rx是x的类型为const int的引用
f(x); // T和param的类型都为int
f(cx); // T和param的类型都为int
f(rx); // T和param的类型都为int
即使cx
和rx
代表const
值,param
仍然不具有const
类型。因为param
是个完全独立于cx
和rx
存在的对象:是cx
和rx
的一个副本
数组实参
template<typename T>
void f(T param); //param为按值传递
const char name[] = "J.P.Briggs"; //name的类型为const char *
f(name); // name是个数组,T的类型推导成const char *
尽管函数无法声明真正的数组类型的实参,它们却能够将形参声明为数组的引用:
template<typename T>
void f(T& param); //按引用方式传递形参的模板
f(name);
T的类型推导结果为const char [13]
,而param
类型被推导为const char(&)[13]
函数实参
void someFunc(int, double);
template<typename T>
void f1(T& param); // param按值传递
template<typename T>
void f2(T& param); // param按引用传递
f1(someFunc); // param被推导为函数指针具体类型为void(*)(int, double)
f2(someFunc); // param被推导为函数指针具体类型为void(&)(int, double)