模板2
函数模板
在调用函数模板时,C编译器通常是根据函数实参的类型来例化函数模板,这个过程由C编译器自动进行,称为模板参数推导(template argument deduction)
调用模板函数时也可显式指定用以替换模板类型参数的模板实参,模板函数的显式调用形式如下:
模板函数名<模板实参表>(函数实参表);
float fx = 6.5 , fy = 7.5;
swap(fx, fy);
int ix = 6, iy = 7;
swap(ix, iy); //错误
函数模板的用法
使用函数模板时,应保证函数的参数与模板函数的参数匹配,编译器不会给模板函数的参数提供任何形式的转换。
事实上,函数模板表示了一组名字相同的函数,这些函数之间,以及这些函数与其它同名函数之间,是重载函数的关系。
在引入模板后,重载函数的参数匹配规则如下
若函数的参数正好和调用函数的参数 匹配,则调用此函数
否则,从函数模板生成函数,如果函数实参正好和生成函数的参数匹配,则调用该模板函数
否则,对相应函数实参做类型转换之后再匹配,如果匹配成功,则调用相应函数(这句话还需理解!!)
否则,编译系统会产生错误提示信息
如下:
# include <iostream>
using namespace std;
template <class T> T max(T a, int b);
double max(double a, int b);
void main()
{
int i1, i2, i3;
double d1, d2, d3;
i1 = 3, i2 = 9;
d1 = 6.7, i3 = 10;
d2 = 8.4, d3 = 7.8;
cout << max(i1, d3) << endl;
cout << max(d1, i3) << endl;
}
double max(double a, int b)
{
cout << "double max(double, int) :";
return a>b ? a : b;
}
template <class T> T max(T a, int b)
{
return a>b ? a : b;
}
cout << max(i1, d3) << endl;//这句话调用了模板函数
这句话先是对比普通函数的形参,发现不匹配,然后去匹配函数模板,不匹配,然后...
显式调用
template <typename T1, typename T2, typename T3>
T1 sum( T2 a, T3 b)
{ return a + b; } ...
int x = 6; float y = 9.9, sigma;
sigma = sum(x, y); //错误
sigma = sum<float,int,float>(x,y); //正确
sigma = sum(x,y); //正确
sigma = sum<,int>(x,y); //错误
省略掉的实参必须是模板参数表尾部的参数对应的实参!
可见,这个时候必须要显式调用,隐式调用根本无法完成推断
类模板
类模板的定义
template<class 类型参数名 (或 typename 类型参数名)>
class 类名
{ …; };
模板参数表中的参数可以有多个,多个参数间用逗号间隔
定义对象
当定义一个模板类的对象时,程序员需要提供模板实参,C++编译器根据程序员提供的模板实参生成一个个具体的适合不同类型的类定义,形式如下
类名<模板实参> 对象名...
StashintStash;
StashfloatStash;
编译器根据模板实参生成类的过程被称为类模板例化
模板参数可以分成两类
类型参数 非类型参数
非类型参数形式如下
类型 参数名...
template<class T, int inc>
class Stash
{ ... };
如果模板参数表中含有非类型参数,定义对象时,程序员需要提供作为实参的常量值。这相当于在程序中定义了一个常量,如:
Stash<int, 50> intStash; // 等同于const int inc = 50
对于非类型参数,可以有默认值。如:
template <class T, int inc = 50 >
class Stash
{ ... };
同函数默认值一样,默认值的指定必须按照从右向左的顺序进行
template <class T, int x=50, int y=100> //正确
template <class T, int x, int y=100> //正确
template <class T, int x=50, int y> //错误
如果指定了默认值,定义对象时,如果程序员没有提供模板实参,则表示使用默认值。如:
stashintStash; // 等同于 stash<int, 50> intStash;
定义成员函数
在类外定义模板类的成员函数,要使用不同的语法,成员函数前必须加上保留字template及模板参数[1]。且第一次出现类名的时侯也应有模板参数表,但此时只列参数名。形式如下:
template <模板参数表>[1]
返回类型 类名<模板参数表>::成员函数名(函数形参表)
{ ... }
在类内定义比较简单,和普通类在类内定义成员函数基本相同,不同之处在于其中可能出现模板参数。
文件关系
函数模板、类模板如果希望被多个程序文件(.cpp)使用,则应在头文件中定义类模板(包括成员函数定义)和函数模板。并在使用类模板或函数模板的程序文件中包含该头文件。因为模板在例化时,需要知道类模板和函数模板的完整定义。
类模板和函数模板之间的关系
#include <iostream.h>
//类模板
template<typename T>
class Test
{ T i;
public:
void set( T ii ) { i = ii; }
void display()
{ cout << "The value of i is: "
<< i << endl; }
};
template <typename S >
Void fun ( Test <S> test )//模板类 做函数参数
{ test.display( ); }
int main( )
{ Test < int > testa;
Test< char > testb;
testa.set(2);
testb.set('c');
fun(testa);
fun(testb);
return 0;
}
类模板与继承
一个类模板在类层次结构中充当的角色:
类模板可以是基类
类模板可以是派生类
类模板可以从模板类派生
类模板可以从非模板类派生
模板类可以从类模板派生
非模板类可以从类模板派生