【C++】浅谈C++模板

我们知道C++是一种“强类型”语言。也就是说,对于一个变量,编译器必须确切知道它是什么类型。但是,这种强类型函数在实现一些简单函数反而更麻烦。例如:求两大数的较大者,应以Max( )函数,我们需要对不同数据类型分别定义不同重载版本来实现:

int Max(int x,int y)        //比较两个int类型的值
{
    return ((x > y) ? x : y);
}
float Max(float x,float y)  //比较两个float类型的值
{
    return ((x > y) ? x : y);
}
double Max(double x,double y)  //比较两个double类型的值
{
    return ((x > y) ? x : y);
}

我们看到,虽然我们可以通过函数重载去实现,但明显存在一些缺点:
1、所有的函数除返回类型外,函数体都相同,代码复用率低;
2、只要新类型出现,我们就需要添加新的对应函数;
3、维护不方便。

这时你可能会想到c语言学到的预处理程序,通过以下方式来实现:

#define Max(x,y)((x > y) ? x : y)

但是同样,我们都知道这不是函数,不会进行函数检测,安全性太低。

那么,这时我们就想,能不能只写一套代码,对于任意类型T的两个对象x、y,函数调用Max(x,y)总能使编译系统理解其比较意义而实现我们编程的目的?
为了解决这个问题,C++引用了模板机制。
什么是模板?
C++程序有类和函数组成,模板分为类模板和函数模板。模板就是把功能相似,仅数据类型不同的函数或类设计为通用的函数模板或类模板,提供给用户。
模板是“泛型编程”的基础。简单的说,类是对象的抽象,而模板是类的抽象,用模板能定义具体类。
函数模板的一般定义形式:

template<typename Paraml,typename Paraml,... ,class Paraml>
返回类型 函数名(函数形参表)
{
}

模板定义以关键字template开始,形参由关键字class或typename及其后面的类型名构成,一般建议尽量使用typename。
注意:不能使用struct代替typename。
模板函数也可以定义为inline函数

template<typename T>
inline T Add(const T _left, const T _right)
{
return (_le _right);
}

注意:inline关键字必须放在模板形参表之后,返回值之前,不能放在template之前
例如:前面的Max()函数可以用模板定义如下:

template<typename T> 
T Max(T x,T y)
{
   return ((x > y) ? x : y);
}

模板是一个蓝图,它本身不是类也不是函数,编译器用模板产生的指定的类或者函数的特定类型版本,产生模板特定类型的过程称为函数模板实例化。
实例:定义一个函数模板,比较两个数大小。

#include<iostream>
using namespace std;
template<typename T>
T Max(T x, T y)
{
    return ((x > y) ? x : y);
}
int main()
{
    cout << Max(10, 20) << endl;
    cout << Max(13.14, 5.2) << endl;
    cout << Max(10, (int)5.2) << endl;
    cout << Max<int>(10, 5.2) << endl;

}

模板实例化过程:
这里写图片描述
具体如下这里写图片描述
模板参数
这里写图片描述
类型参数可以用来指定返回类型或函数的参数类型,以及在函数体内用于变量的声明或类型的转换:

template<typename T>
T foo(T* p)
{
   T tmp = *p;
   return tmp;
}

模板形参的名字在同一模板形参列表中只能使用一次

template <typename T,typename T>
void fun(T t1, T t2)
{
}

这里写图片描述
所有模板形参前面必须加上class或者typename关键字修饰

template <typename T,U>
void fun(T t, U u)
{
}

这里写图片描述
非类型模板参数
一个非类型参数表示一个值而非一个类型。非模板类型形参是模板内部定义的常量,在需要常量表达式的时候,可以使用非模板类型参数。
例如数组长度:
这里写图片描述
说明:
1、一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例
化为这个非模板函数。
2、对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调动非模板
函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数,
那么将选择模板。
3、显式指定一个空的模板实参列表,该语法告诉编译器只有模板才能来匹配这个调用,
而且所有的模板参数都应该根据实参演绎出来。
4、模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。

模板函数特化
特化的一般形式:
1、关键字template后面接一对空的尖括号<>
2、函数名后接模板名和一对尖括号,尖括号中指定这个特化定义的模板形参
3、函数形参表
4、函数体

template<>
返回值 函数名<Type>(参数列表)
{
// 函数体
}

例如,对于前面的Max()函数模板。

template<class T>
T Max(T x,T y)
{
   return ((x>y) ? x:y);
}

如果比较的是两个“const char*”类型,那么函数模板用“const char*”型的模板实参实例化。如果还想让每个实参都被解释为C风格的字符串而不是字符指针,那么饿通过模板定义给出的语义就不正确了,必须为函数模板实例化提供“const char*”的特化:

#include<cstring>     //引入cstring的相关声明
template<>           //特化标志
const char* Max<const char*>(const char* x,const char* y)            //用const char*转化
{
    return (strcmp(x,y)<0)?x:y;
}

特化的声明必须与特定的模板相匹配,否则:

template<>
int compare<int>(int);

这里写图片描述
由于有了这个特化,程序中对所有用两个“const char*”型形参进行调用的Max()都会调用这个特化的定义,而对于其他的调用,则通过模板定义实例化对象:

const char* a = “hello”;
const char* b = "world";
int x = 10;
int y = 20;
Max(a,b);      //调用模板的特化版本进行实例化
Max(x,y);      //调用模板的通用版本进行实例化  

注意:在模板特化版本的调用中,实参类型必须与特化版本函数的形参类型完全匹配,如果不匹配,编译器将为实参模板定义中实例化一个实例。
这里写图片描述
注意:特化不能出现在模板实例的调用之后,应该在头文件中包含模板特化的声明,然后使用该特化版本的每个源文件包含该头文件。。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值