您的位置:首页 > 编程语言 > C语言/C++

C++模板template总结

2013-10-19 11:26 417 查看
有以下这样3个求加法的函数: 

1 int Add(int x,int y)
2 {
3 return x+y;
4 }
5
6 double Add(double x,double y)
7 {
8 return x+y;
9 }
10
11 long Add(long x,long y)
12 {
13 return x+y;
14 }


它们拥有同一个函数名,相同的函数体,却因为参数类型和返回值类型不一样,所以是3个完全不同的函数。即使它们是二元加法的重载函数,但是不得不为每一函数编写一组函数体完全相同的代码。如果从这些函数中提炼出一个通用函数,而它又适用于多种不同类型的数据,这样会使代码的重用率大大提高。那么C++的模板就可解决这样的问题。模板可以实现类型的参数化(把类型定义为参数),从而实现了真正的代码可重用性。C++中的模板可分为函数模板和类模板,而把函数模板的具体化称为模板函数,把类模板的具体化成为模板类。

  1.函数模板就是建立一个通用的函数,其参数类型和返回类型不具体指定,用一个虚拟的类型来代表。函数模板的声明格式:

  template<typename 类型参数>

  返回类型 函数名(模板形参表)

  {

    函数体

  }

  或

  template<class 类型参数>

  返回类型 函数名(模板形参表)

  {

    函数体

  }

template是一个声明模板的关键字,类型参数一般用T这样的标识符来代表一个虚拟的类型,当使用函数模板时,会将类型参数具体化。typename和class关键字作用都是用来表示它们之后的参数是一个类型的参数。只不过class是早期C++版本中所使用的,后来为了不与类产生混淆,所以增加个关键字typename。下面我就对上述3个加法函数进行函数模板化:


View
Code

结果:



看着上述的代码中,是否觉得函数模板的声明和C#中的泛型使用有点相像呢,当调用函数模板时(如:Add(10,10))就是对函数模板的具体化(如:int Add(int,int)),具体化的函数模板就是模板函数。在函数模板中类型参数也可以指定多个,只不过定义的每个类型参数之前都必须有关键字typename(class)。


View
Code

结果:



在定义函数模板时要注意的一点是在template语句和函数模板定义语句之间是不允许插入其他的语句的。和一般函数一样,函数模板也可以重载:


View
Code

结果:



函数模板与同名非模板函数也可以重载。比如:


View
Code

结果:



就如示例代码运行的结果表明的一样,当模板函数和同名的非模板函数重载时,首先寻找与参数类型完全匹配的非模板函数,找到了,则调用它,如果没找到,则寻找函数模板,找到后具体化函数模板,而后调用该模板函数。

 2.和函数模板一样,类模板就是建立一个通用类,其数据成员的类型、成员函数的返回类型和参数类型都不具体指定,用一个虚拟类型来代表。当使用类模板建立对象时,系统会根据实参的类型来取代类模板中的虚拟类型从而实现不同类的功能。其定义格式为:

  template <typename 类型参数>

  class 类名

  {

    类成员声明

  }

  或

  template <class 类型参数>

  class 类名

  {

    类成员声明

  }

在类成员声明里,成员数据类型、成员函数的返回类型和参数类型前面需加上类型参数。在类模板中成员函数既可以定义在类模板内,也可以定义在类模板外,在定义类模板外时C++有这样的规定:需要在成员函数定义之前进行模板声明,且在成员函数名之前加上“类名<类型参数>::”:

  template <typename(class) 类型参数>

返回类型 类名<类型参数>::函数名(形参)

{

  函数体

}

而类模板定义对象的形式:

  类模板名<实际类型> 对象名;

  类模板名<实际类型> 对象名(实参);

示例说明如下:



1 #include "stdafx.h"
2 #include <iostream>
3 #include <string>
4
5 template <typename T>//在类模板定义之前,都需要加上模板声明
6 class BinaryOperation//二目运算类
7 {
8 private:
9         T x;
10         T y;
11 char op;
12 void add()
13         {
14             std::cout<<x<<op<<y<<"="<<x+y<<std::endl;
15         }
16 void sub()
17         {
18             std::cout<<x<<op<<y<<"="<<x-y<<std::endl;
19         }
20 void mul();
21 void div();
22 public:
23         BinaryOperation(T x,T y):x(x),y(y)
24         {
25         }
26 void determineOp(char op);
27 };
28
29
30 //在类外定义成员函数:
31 //在成员函数定义之前进行模板声明,
32 //且在成员函数名之前加上"类名<类型参数>::"
33 template <typename T>
34 void BinaryOperation <typename T>::mul()
35 {
36     std::cout<<x<<op<<y<<"="<<x*y<<std::endl;
37 }
38
39 template <typename T>
40 void BinaryOperation <typename T>::div()
41 {
42
43     std::cout<<x<<op<<y<<"="<<x/y<<std::endl;
44 }
45
46 template <typename T>
47 void BinaryOperation <typename T>::determineOp(char op)
48 {
49 this->op=op;
50 switch(op)
51     {
52 case'+':
53             add();
54 break;
55 case'-':
56             sub();
57 break;
58 case'*':
59             mul();
60 break;
61 case'/':
62             div();
63 break;
64 default:
65 break;
66     }
67 }
68
69 int main()
70 {
71
72     BinaryOperation<int> op(10,10);
73     op.determineOp('+');
74     op.determineOp('-');
75     op.determineOp('*');
76     op.determineOp('/');
77
78 return0;
79 }


结果:



和函数模板一样,类模板也允许定义多个类型参数,这里不再一一举例了,有兴趣的朋友可以参考上述的示例代码尝试一下。

转自/article/5199407.html

二、template的实例化

实例化:一个通过使用具体值替换模板参数,从模板产生的普通类,函数或者成员函数的过程。

函数模板定义出来以后我们可以指定泛化类型T具体使用哪里数据类型,这样子编译器可以利用这个函数模板生成一个正式的函数,这个过程就称之为函数模板的实例化,下面的代码就实现了函数模板的实例化.

Max<int>(1, 2)

Max<double>(1.1,2.2)

函数调用足以判断参数类型的话,编译器会自动实例化相应的数据类型函数.如 Max(1.1,2.2)

上面Max函数模板只是使用了一个泛化类型的模板,所以我们无法用来比较不同数据类型的数的大小,下面我使用了两个泛化类型,这样子就不要求比较的数据必须是同一个数据类型了,(当然这个Max函数模板对于不同数据类型的比较只是简单的判断而已,有待改进)代码如下:

#include <stdio.h>
template <class T1, class T2>
T1 Max(T1 a, T2 b){
return static_cast<T1>(a > b ? a : b);
}
int main(void){
printf("%f\n", Max(1.1, 2));
return 0;
}


运行结果:

2.000000

二、template的特化

类模板特化的意思是,对于某个特定的类型,需要对模板进行特殊化,即特殊的处理。例如,stack类模板针对bool类型有特化,因为实际上bool类型只需要一个二进制位,就可以对其进行存储,使用一个字或者一个字节都是浪费存储空间的.同样,函数模板特化也是针对某个特定类型的特殊处理,一个比较经典的例子:

template <class T>
T mymax(const T t1, const T t2)
{
return t1 < t2 ? t2 : t1;
}
main()
{
int highest = mymax(5,10);//正确结果
char c = mymax(‘a’, ’z’);//正确结果
const char* p1 = “hello”;
const char* p2 = “world”;
const char* p = mymax(p1,p2);//错误结果,因为比较的是指针,而不是内容
}


如果需要得到正确结果就需要针对const char*的函数模板特化:

const char* mymax(const char* t1,const char* t2)
{
return (strcmp(t1,t2) < 0) ? t2 : t1;
}


另外的例子如下:

template <class T>
T Square(T x)
{
return x*x;
}

const char* Square(const char* x)
{
char* ret = (char*)(malloc(strlen(x)*2+1));
strcpy(ret,x);
strcat(ret,x);
return ret;
}
int main()
{
cout<<Square(9)<<endl;
cout<<Square(1.5)<<endl;
cout<<Square("hello")<<endl;
return 0;
}



二、模板偏特化,partial specialization of template

模板的偏特化是指需要根据模板的某些但不是全部的参数进行特化。
1. 类模板的偏特化
例如c++标准库中的类vector的定义
template <class T, class Allocator>
class vector { // … // };
template <class Allocator>
class vector<bool, Allocator> { //…//};
这个偏特化的例子中,一个参数被绑定到bool类型,而另一个参数仍未绑定需要由用户指定。

2. 函数模板偏特化
严格的来说,函数模板并不支持偏特化,但由于可以对函数进行重载,所以可以达到类似于类模板偏特化的效果。
template <class T> void f(T); (a)
根据重载规则,对(a)进行重载
template <class T> void f(T*); (b)
如果将(a)称为基模板,那么(b)称为对基模板(a)的重载,而非对(a)的偏特化。C++的标准委员会仍在对下一个版本中是否允许函数模板的偏特化进行讨论。

三、模板特化时的匹配规则
(1) 类模板的匹配规则
最优化的优于次特化的,即模板参数最精确匹配的具有最高的优先权
例子:
template <class T> class vector{//…//}; // (a) 普通型
template <class T> class vector<T*>{//…//}; // (b) 对指针类型特化
template <> class vector <void*>{//…//}; // (c) 对void*进行特化
每个类型都可以用作普通型(a)的参数,但只有指针类型才能用作(b)的参数,而只有void*才能作为(c)的参数
(2) 函数模板的匹配规则
非模板函数具有最高的优先权。如果不存在匹配的非模板函数的话,那么最匹配的和最特化的函数具有高优先权
例子:
template <class T> void f(T); // (d)
template <class T> void f(int, T, double); // (e)
template <class T> void f(T*); // (f)
template <> void f<int> (int) ; // (g)
void f(double); // (h)
bool b;
int i;
double d;
f(b); // 以 T = bool 调用 (d)
f(i,42,d) // 以 T = int 调用(e)
f(&i) ; // 以 T = int* 调用(f)
f(d); // 调用(h)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: