函数模板大致形如:
template <typename T>
void f(ParamType param);
调用形如:
f(expr);
在编译期,编译器会通过expr推导T
和ParamType的类型,ParamType通常包含一些修饰词,如 const
或引用等限定词。
T
的类型推导结果不仅依赖expr的类型,还依赖ParamType的形式。需要分三种情况讨论。
类型推导会这样运作:
- 若expr有引用类型,则先将引用忽略。
- 然后对expr的类型和ParamType的类型执行模式匹配,来决定T的类型。
例如,模式如下:
template <typename T>
void f(T& param);
又声明了如下变量:
int x = 27;
const int cx = x;
const int& rx = x;
类型推导结果如下:
expr | T | ParamType |
---|---|---|
x |
int |
int& |
cx |
const int |
const int& |
rx |
const int |
const int& |
形参是右值引用或指针的类型推导运作方式是完全相同的。
- 如果expr是个左值,
T
和ParamType都会被推导为左值引用,这是在模板类型推到中T
被推导为引用的唯一情形。 - 如果expr是个右值,则应用情况1的常规规则。
例如:
template <typename T>
void f(T&& param);
expr | T | ParamType |
---|---|---|
x |
int& |
int& |
cx |
const int& |
const int& |
rx |
const int& |
const int& |
27 |
int |
int&& |
即按值传递:
template <typename T>
void f(T param);
- 若expr具有引用类型,则忽略其引用部分。
- 忽略expr的引用性后,去除expr的cv限定。
expr | T | ParamType |
---|---|---|
x |
int |
int |
cx |
int |
int |
rx |
int |
int |
有些情况下数组会退化为指向首元素的指针。函数无法正确声明真正的数组类型的形参,但它们却能够将形参声明为数组的引用。如果我们按引用传递实参:
template <typename T>
void f(T& param);
然后,向其传递一个数组
const char* name[] = "J. P. Briggs";
f(name);
在这种情况下,T会被推导为 const char [13]
,而param被推导为const char (&)[13]
。
函数类型也同样会退化为函数指针。
void someFunc(int, double);
template <typename T>
void f1(T param);
template <typename T>
void f2(T& param);
函数 | param类型 |
---|---|
f1 |
void (*)(int, double) |
f2 |
void (&)(int, double) |