一. 使用函数模板
先看一组代码,功能是交换两个数:
void swapr(int &a, int &b)
{
int temp;
temp = a;
a = b;
b = temp;
}
void swapp(int *p, int *q)
{
int temp;
temp = *p;
*p = *q;
*q = temp;
}
void swapr(int a, int b)
{
int temp;
temp = a;
a = b;
b = temp;
}
如果要交换两个double类型的值,一种方法是把int替换成double,定义一个新的方法。但是这样不仅容易出错,还要花费额外的时间来维护检查。所以需要用到函数模板:
template <typename T>
void Swap(T& a, T& b)
{
T temp;
temp = a;
a = b;
b = temp;
}
二. 重载函数模板
和普通函数一样,通过修改函数特征标达到重载的目的:
template <typename T>
void Swap(T a[], T b[], int n)
{
T temp;
for (int i = 0; i < n; i++)
{
temp = a[i];
a[i] = b[i];
b[i] = temp;
}
}
三. 函数模板的局限性
如果函数模板里出现了<,>,*等运算,对于指针,数组或者结构体的类型来说,并不适用。
四. 显示具体化
同样的模板函数名下,需要针对某些类型做出不同的算法。因此c++标准给出了显示具体化的机制:
- 一个函数名,可以同时存在非模板函数,模板函数,显示具体化模板函数以及它们的重载版本;
- 显示具体化的原型要以template<>开头,并通过名称指出类型;
- 编译器选择的优先级:非模板函数 > 具体化模板函数 > 模板函数;
以下是交换函数的非模板函数,模板函数和具体化的原型:
struct job{
char name[40];
double salary;
int floor;
};
// non template function prototype
void Swap(job &, job &);
// template prototype
template <typename T>
void Swap(T &, T &);
// explicit specialization for type job
template <>
void Swap<job>(job &, job &); // 只交换salary和floor成员
五. 显式实例化
先了解这几个术语:显式具体化,隐式实例化,显式实例化。
函数模板或者具体化模板都是给出一个函数定义的方案。编译器使用模板生成函数定义的时候叫做实例化。编译器根据参数类型调用相应模板生成函数定义叫做隐式实例化。
显式实例化就是命令编译器直接生成特定类型的函数定义,语法是:
template void swap<int>(int, int);
还可以这么写:
template <typename T>
T add(T a, T b)
{
return a + b;
}
...
int m = 6;
double n = 12.1;
cout << template<double>add(m, n) << endl;
六. c++11标准对函数模板的一些改进
decltype声明
template<class T1, class T2>
void fd(T1 x, T2 y)
{
?Type? xpy = x + y;
}
xpy的类型是无法确定的。因为double+int = double,int + short = int, char + short = int,如果是结构体或者类那么更不能确定了。
c++11标准给出了decltype关键字:
template<class T1, class T2>
void fd(T1 x, T2 y)
{
decltype(x+y) xpy = x + y;
}
那么decltype(expression) var类型是什么?
编译器会按以下规则推断var的类型:
a. 如果expression是一个没有括号的标识符,那么var类型和标识符相同(包括const限定);
double x = 5.5;
double y = 7.9;
double &rx = x;
const double *pd;
decltype(x) w; // w is type double
decltype(rx) u = y; // u is type double &
decltype(pd) v; // v is type const double *
b. 如果expression是一个函数调用,那么var类型和函数返回类型相同;
long indeed(int);
decltype (indeed(3)) m; // m is type int
c. 如果expression是一个左值,那么var类型是expression类型的引用;
double xx = 4.4;
decltype ((xx)) r2 = xx; // r2 is double &
decltype(xx) w = xx; // w is double 匹配条件a
d. 如果以上条件都不满足,那么var类型和expression类型相同;
int j = 3;
int &k = j;
int &n =j;
decltype(j+6) i1; // i1 type int
decltype(100L) i2; // i2 type long
decltype(k+n) i3; // i3 type int;
template<class T1, class T2>
void ft(T1 x, T2 y)
{
typedef decltype(x + y) xytype;
xytype xpy = x+y;
xytype arr[10];
xytype & rxy = arr[2];
}
后置返回类型
template<class T1, class T2>
?type? gt(T1 x, T2 y)
{
return x + y;
}
编译器无法预知返回类型,因为x和y不在作用域内了。
因此有了一种新的语法声明函数返回类型:
double h(double a, float b);
auto h(double a, float b)->double;
template<class T1, class T2>
auto gt(T1 x, T2 y)->decltype(x+y)
{
return x+y;
}