1.关键字class和typename
这两个关键字都是一样的用法。这与提出的历史有关,先是用的class。最后为了与类class关键字区分开,引入了新的关键字 typename
2.申明一个函数模板
template<class T>
T func(T x,int y)
{
T x;
//....
}
调用程序代码段可以如下:
double d;
int a;
func(d,a);
调用时将使用实参d的数据类型double去代替函数模板中的T。
double fuc(double x,int y)
{
double x;
//……
}
函数模板只是申明了一个函数的描述即模板,不是一个可以直接执行的函数,只有根据实际情况用实参的数据类型代替类型参数之后,才能产生真正的函数。
具体的类模板申明如下:
template<class 数据类型参数标识符1,…,class 数据类型参数标识符n>
函数模板的异常处理
函数模板中模板形参可实例化为各种类型,但当实例化模板实参之间不完全一致时,就可能发生错误,如:
template<typename T>
void min(T &x, T &y)
{ return (x<y)?x:y; }
void func(int i, char j)
{
min(i, i);
min(j, j);
min(i, j);
min(j, i);
}
调用过程中,前两个调用没有问题,后两个调用出现错误。原因是编译器按最先遇到的实参的类型隐含生成一个模板函数,比如调用 min(i, j); T类型变成了int。。与后面的参数类型char不匹配,产生了错误。这在编译的时候就会报错。
解决上述问题的方法有如下几种常见的方式:
- 采用强制类型转换为相同类型 如改写为
min(i,int(j))
用非模板函数重载函数模板
- 1.借用函数模板的函数体
此时只声明非模板函数的原型,它的函数体借用函数模板的函数体。如改写上面的例子如下:
template<typename T>
void min(T &x, T &y)
{ return (x<y)?x:y; }
int min(int,int);
void func(int i, char j)
{
min(i, i);
min(j, j);
min(i, j);
min(j, i);
}
就不会出错了,因为重载函数支持数据间的隐式类型转换。
- 2.重新定义函数体
但是函数模板与同名的非模板函数重载时,应遵循下列调用原则:
若参数完全匹配的函数多于一个,则这个调用是一个错误的调用
3.申明一个类模板
定义一个类模板如下:
template<class T>
class Test{
private:
T n;
const T i;
static T cnt;
public:
Test():i(0){}
Test(T k);
~Test(){}
void print();
T operator+(T x);
};
在类定义体外定义成员函数时,若此成员函数中有模板参数存在,则除了需要和一般类的体外定义成员函数一样的定义外,还需在函数体外进行模板声明
例如
template<class T>
void Test<T>::print(){
std::cout<<"n="<<n<<std::endl;
std::cout<<"i="<<i<<std::endl;
std::cout<<"cnt="<<cnt<<std::endl;
}
如果函数是以通用类型为返回类型,则要在函数名前的类名后缀上“”。例如:
template<class T>
Test<T>::Test(T k):i(k){n=k;cnt++;}
template<class T>
T Test<T>::operator+(T x){
return n + x;
}
在类定义体外初始化const成员和static成员变量的做法和普通类体外初始化const成员和static成员变量的做法基本上是一样的,唯一的区别是需再对模板进行声明,例如
template<class T>
int Test<T>::cnt=0;
template<class T>
Test<T>::Test(T k):i(k){n=k;cnt++;}
类模板的使用 类模板的使用实际上是将类模板实例化成一个具体的类,它的格式为:类名<实际的类型>