C++函数模板
1 2
3 4 5 6 7 8 |
template < typename T> void Swap(T &a ,T &b) {
T temp; temp = a; a = b; b = temp; } |
在使用模板函数时,编译器根据实际的类型生成相应的函数定义。
重载的模板
并非所有的类型都使用相同的算法,可以像重载常规函数那样重载模板函数定义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
template < typename T> void Swap(T &a ,T &b); //#1 template < typename T> void Swap(T *a ,T *b, int n); //#2 最后一个参数是具体类型 int main() {
int i =10,j=20; Swap(i,j); //使用#1 const int Lim = 8; int d1[Lim]={0,1,2,3,4,5,6,7}; int d2[Lim]={7,6,5,4,3,2,1,0}; Swap(d1,d2,Lim); //使用#2 } 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; } } |
模板局限性
某些时候,类型T的相应操作只适用于数组,如果T为结构体则模板函数便不成立
同样,如if(a>b)
,如果T为结构,则>便不成立
解决方案:
显示具体化
当编译器找到与函数调用匹配的具体化定义时,将使用该定义,不再寻找模板。
- 对于给定的函数名,可以有非模板函数、模板函数和显示具体化模板函数以及各自的重载版本。
- 显示具体化的原型和定义以
template<>
开头,并通过名称来指出类型
- 调用顺序是:非模板函数>具体化模板函数>模板函数
1 2 3 4 5 6 7 8 |
void Swap(job& ,job&); template < typename T> void Swap(T&,T&); template <> void Swap<job>(job& ,job&); //显示具体化 //Swap<job>中<job>是可选的,因为函数的参数类型表明,这是job的一个具体化,所以也可以这样写: template <> void Swap(job& ,job&); |
实例化和具体化
注意:函数模板并不会生成函数定义,他只是生成一个用于生成函数定义的方案,编译器使用模板为特定的类型生成函数定义时,得到的是模板实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
template < typename T> void Swap(T &a ,T &b); int a =10,b=20; Swap(a,b); //因为提供了int类型的参数,所以自动生成了int类型的模板实例。这样是==隐式实例化== //也可以直接命令编译器创建特定的实例 //显示实例化 template void Swap< int >( int &, int &); //使用Swap()模板生成int类型的函数定义 //显示具体化 template <> void Swap< int >( int & , int &); template <> void Swap( int & , int &); //区别在于:具体化是不使用Swap()模板函数生成函数定义,而是使用专门为int类型显示定义的函数定义 //简单的理解,具体化是对函数的声明,而实例化是对模板函数的使用 |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
template < typename T> T Add(T a,T b) {
return a+b; } int m=6; double x=10.5; Add< double >(x,m); //与Add(x,m)不匹配,因为一个是int一个是double //通过Add<double>实例化,可强制将m转为double //但是同样的对Swap便不能成功,因为Swap中使用的是引用类型 Swap< double >(m,x); //double& 不能指向int |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//使用案例 template < typename T> void Swap(T &,T &); template <> void Swap<job>(job&,job&); //具体化 int mian() {
template void Swap< char >( char & , char &); short a,b; Swap(a,b); //隐式实例化 job n,m; Swap(n,m); //显示具体化 char g,h; Swap(g,h); //显示实例化 } |
模板函数类型的确定
1 2 3 4 5 |
template < class T1, class T2> void fun(T1 x,T2 y) {
?type? s=x+y; //因为是模板函数,此时?type?类型不确定 } |
C++11增加decltype
关键字
1 2 3 4 5 |
template < class T1, class T2> void fun(T1 x,T2 y) {
decltype(x+y) s=x+y; //s类型与x+y的类型一致 } |
使用decltype(expression) var
的步骤:
1.如果expression没有用括号括起来,则var与expression类型相同,包括const等限定符
1 2 3 4 5 6 |
double x =5.5; double & z =x; const double * pd; decltype(x) w; //w为double类型 decltype(z) u; //u为double& 类型 decltype(pd) v; //v为const double* 类型 |
2.如果expression是一个函数调用,则var与返回值类型相同。并不会实际调用函数,编译器通过查看原型来确定返回值类型
3.如果expression是一个左值,则var为指向其类型的引用。常见的情况如下:
1 2 3 4 5 6 |
double x = 4.5; decltype((x)) r = x; //r是double&类型 decltype(x) r = x; //r是double类型 //括号不会改变expression的值和左值性 //可理解为加括号仅仅是decltype声明引用的一种方式 |
4.如果前3条都不满足,则var与expression类型相同
1 2 3 4 5 6 |
int j=3; int &k=j; int &n=j; decltype(j+6) x; //x是int decltype(k+n) y; //y是int ,虽然k和n是引用,但是k+n不是引用是2个int的和 |
如果多次声明,可以结合typedef
和decltype
1 2 3 |
typedef decltype(x+y) xytype; xytype z = x+y; xytype arr[10]; |
但是某些需定义返回值类型的函数模板任然不能得到解决,如:
1 2 3 4 5 |
template < class T1, class T2> ?type? fun(T1 x,T2 y) //此时无法确定类型 {
return x+y; } |
C++新增语法auto h(int x,float y) -> double
,这称为后置返回类型,auto是一个占位符
1 2 3 4 5 |
template < class T1, class T2> auto fun(T1 x,T2 y)->decltype(x+y) //后置类型使用decltype {
return x+y; } |