标签:
C++程序是一些类型和函数,编程就是设计类型和函数,然后将它们按C++的程序结构组织起来。由于事物的相似性,设计的类型和函数有时也表现出相同性。将这些相似的类型和函数归纳起来构成一个类簇和函数簇,用一种统一的方式来编程就是模板编程。由模板可以得到一系列的相似类型或相似函数,这些相似类型和相似函数涉及到的数据类型可能不同,但处理数据却具有相同的表现形态。这些相似类型和相似函数就是将涉及到的数据之类型为参数来生成的模板,其相似类型称为模板类,相似函数称为模板函数。
一·函数模板(Function Templates)
考察两个交换函数swap,一个swap交换两个int类型的值,一个swap交换两个double类型的值,分别如下:
void swap(int& a,int& b)
{
int temp = a;
a = b;
b = temp;
}
void swap(double& a,double& b)
{
double temp = a;
a = b;
b = temp;
}
事实上,交换任何两个Type类型的对象,都有下列函数定义形式:
void swap(Type& a,Type& b)
{
Type temp = a;
a = b;
b = temp;
}
不同的Type类型,可以写出不同的swap函数,这些交换函数都是重载的,或者说,都是函数同名的。这上系列swap重载函数,随着所处理的数据类型的变更,其函数实体会像一个个零星的小碎片一样,分散在需要该操作的程序上下文中,增加了工作量。
1.函数模板的定义(Define Function Template)
函数模板的定义形式为
template <tempname 类型参数表>
返回类型 函数模板名(数据参数表)
{
函数模板定义体
}
template后面用尖括号括起来的"类型参数表",描述函数模板"函数模板名"的模板形式参数(简称模板形参)。模板形参是类型形式的,可以是基本数据类型,也可以是类类型。每个模板形参都必须加上前缀class或typename(tempname更确切).在template描述的模板形参之后是函数模板的定义体,它包括模板返回在类型,函数模板名,函数模板之数据形参,以及函数模板定义体。上面的swap函数族可以写成函数模板:
template<typename T>
void swap(T& a,T& b)
{
T temp = a;
a = b;
b = temp;
}
其中,函数模板名为swap,模板形参为T,函数模板的数据形参为a和b,函数模板的返回类型为void,函数模板的定义体为一对花括号中间的内容。
函数模板名后面圆括号中的数据形参一般要用到template后面的模板形参名T,也就是具有模板形参T的对象或变量实体。这里的swap函数模板中,数据形参表就是由具有T的引用类型的对象a和b构成的。
函数模板不是函数,它是以具体的类型为实参来生成函数体的模板。函数模板定义被编译时,不会产生任何执行代码。函数模板定义只是对未来生成的函数体的描述,表示它每次能单独处理在模板形参表中说明的数据类型。
2.函数模板的用法(Using Function Templates)
使用函数模板,就是以函数模板名为函数名的函数调用。其形式为:
函数模板名(数据实参表);
第一次使用函数模板时,会触发编译器产生一个对应函数模板的函数体定义。当编译器发现有一个函数模板名为函数名的调用时,将根据数据实参表中的对象或变量的类型,确认是否匹配函数模板中对应的数据形参表,然后生成一个函数。该函数的定义体与函数模板的定义体相同,而数据形参表的类型则以数据实参表的类型为依据。该函数称为模板函数。例:
1.#include<iostream>
2.using namespace std;
3.template<typename T>
4.void swap(T& a,T& b)
5.{
6. T temp = a;a = b;b = temp;
7.}
8.int main()
9.{
10. double dx = 3.5,dy = 3.6;
11. int ix = 6,iy = 7,ia = 303,ib = 505;
12. string s1 = "good",s2="better";
13. cout<<"double dx="<<dx<<", dy="<<dy<<"\n";
14. cout<<"int ix ="<< ix<<", iy ="<< iy<<"\n";
15. cout<<"string s1 =\"<<s1<<"\", s2 =\""<<s2<<"\"\n";
16. swap(dx,dy);
17. swap(ix,iy);
18. swap(s1,s2);
19. swap(ia,ib);
20. cout<<"\after swap:\n";
21. cout<<"double dx="<<dx<<", dy="<<dy<<"\n";
22. cout<<"int ix="<<ix<<x, iy="<<iy<<"\n";
23. cout<<"string s1=\""<<s1<<"\",s2=\""<<s2<<"\"\n";
24.}
编译器在看到第16行的swap(dx,dy)时,因为是首次看到double型实参的模板名swap的函数调用,所以生成函数名为swap<double>的模板函数,即生成如下形式的函数定义:
void swap<double>(double& a,double& b)
{
double temp= a;a = b;b = temp;
}
由于dx和dy是double型的,因此该double类型再推演,得到其模板实参为double型。因此模板函数得以命名为swap<double>。同时,数据实参dx和dy作为初值赋给了数据形参a和b.根据数据实参的类型----匹配数据形参的类型---确认模板实参----推得模板形参的过程称为数据实参的演绎。
以函数模板名为函数名的函数调用,以数据实参dx和dy推演函数模板实参,进而生成模板定义的过程称为函数模板的实体化或实例化。同样,在第17行中的swap(ix,iy)调用时,生成swap<int>模板函数定义。但是,当第19行上又一次看到swap(ia,ib)的函数调用时,由于系统中已经存在int型的swap模板函数,所以就不再生成swap<int>的模板函数定义了。模板函数定义也是函数定义的一种,必须符合C++函数的一次定义规则。
显然,一个函数模板可以生成许多不同的模板函数,如函数模板swap生成了模板函数swap<double>和swap<int>。因此,一个函数模板所能生成的都是不同名称的模板函数,模板实参不同,则生成的模板函数也不同,一个函数模板所反映的是不同函数的函数族,它们因类型实参不同而不同。
二.函数模板参数(Function Templae Parameters)
1.苛刻的类型匹配
模板函数调用是寻求函数模板的类型参数匹配,类型实参与类型形参的匹配规则与函数的数据实参类型与数据形参类型匹配规则不同。类型实参与类型形参匹配规则更苛刻。例:
1.template<typename T>
2.void swap(T& a,T& b)
3.{
4. T temp = a;a = b;b = temp;
5.}
6.int add(double a,double b)
7.{
8. return a+b;
9.}
int main()
{
int ia = 3;
double db = 5.0;
char s1[] = "good",s2[]="better";
int x = add(ia,db); //ok
swap(ia,db); //NG
swap(s1,s2); //NG
}
标签:
原文地址:http://www.cnblogs.com/fenghuan/p/4791073.html