模板

模板的基本概念
什么是模板

模板是C++支持参数化多态的工具,使用模板可以使用户为类或函数声明一种模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。

  • 模板是C++的一种特性,允许函数或类通过泛型的形式表现或运行。
  • 模板可以使得函数或类在对应不同的型别的时候正常工作,而无需为每一份代码都写一份代码。
  • 模板是一种泛型编程的机制,也是一种代码复用的手段。
  • 模板是通用语言的特性,模板又叫参数化类型。
模板的作用

实现C++的泛型机制,摆脱对类型的依赖,实现通用性

模板的类型
函数模板

函数模板,实际上是建立一个通用函数,它所用到的数据的类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位),**等发生函数调用时再根据传入的实参来逆推出真正的类型。这个通用函数就称为函数模板(Function Template)。函数模板中定义的类型参数可以用在函数声明和函数定义

类模板

C++ 除了支持函数模板,还支持类模板(Class Template)。类模板中定义的类型参数可以用在类声明和类实现中。类模板的目的同样是将数据的类型参数化。

模板实例化和实参推演

在调用点,通过具体的类型替换虚假的类型,整个替换过程就是实例化过程。

编译器调用模板函数时,编译器会根据实参的类型,推演出模板的类型,并再生产相应的代码,称为模板的实参推演

A<int> a;  //类模板只能显式实例化,没法推演 
fun<int>(1,2);  //函数模板的显式实例化,不需要推演
fun(1,2)    //函数模板的隐式实例化,需要进行推演

在实参推演时必须注意:不能产生二义性,必须有实参

类模板的实例化

  • 不允许实参推演
  • 类模板的实例化是选择实例化(使用哪个函数,才会去实例化哪个函数)
模板类型参数

类型参数由关键字class或typename后接说明符构成

template<class T>
void fun(T a){}

T就是一个类型形参,类型形参的名字用户可自定义

template<class T>
void fun(T a, T b){},
// fun(12,56.2)  == > error

语句调用fun(12,56.2)将出错,因为该语句给同一模板形参T指定了两种类型,第一个实参2把模板形参T推演成为int,而第二个实参3.2把模板形参推演为double,两种类型的形参不一致,编译器会产生二义性,会出错。

模板非类型参数

即就是内置类型参数

template<class T, int a> 
class B{};
  • 必须是常量
  • 不能是浮点类型和类类型。

sizeof表达式的结果是一个常量表达式,也能用作非类型模板形参的实参

模板的编译

分为两个阶段:

  • 定义点只编译模板的头部
  • 在调用点编译实例化后的模板函数(针对函数模板来说)
模板特例化

针对特殊的类型,模板无法满足特殊需求时,我们就可以提供模板特例化版本:特殊的实例化

//无法满足需求时

template<typename T>
bool Compare(const T a,const T b)								const 修饰 a,b
{
	cout<<"template<typename T>Compare(T,T)"<<endl;      
	return a>b;
}

//实现特例化版本

template<>
bool Compare<char *>(const char* a,const char* b)				const 修饰*a  *b
{
	cout<<"template<char*>Compare(char*,char*)"<<endl;
	return strcmp(a,b)>0;
}

特例化又称为专用化

完全特例化(全特化)

某个版本对一个类型不能满足需求时,就要做完全特例化
C++ 98 函数模板只支持完全特例化

部分特例化(偏特化)

某个版本对一部分类型无法满足需求时,针对一部分特殊处理就是部分特例化

模板重载

指普通函数版本、模板版本、模板特例化版本可以共存

  • 普通函数版本 优先
  • 模板特例化版本 次优先
  • 模板版本 最后

要求类型必须完全匹配

bool  Compare(char *a,char *b)
{
	return strcmp(a,b)>0;
}

template<typename T>
bool Compare(T a,T b)
{
	cout<<"template<typename T>Compare(T,T)"<<endl;
	return a>b;
}

template<>
bool Compare<char *>(char* a,char* b)
{
	cout<<"template<char*>Compare(char*,char*)"<<endl;
	return strcmp(a,b)>0;
}
int main()
{
	Compare("hello","world");//模板实例化 
	//针对这个字符串 是常量   当模板实参推演时  const char*  普通函数不符合  模板特例化不符合  然后就是模板实例化  
	/*	

}

1.为什么我们要把模板的整个定义写在.h文件中?

普通函数(声明写在.h,定义实现写在源文件)
在函数包含中不会太大,并且只需要编译一次,甚至可以通过共享库加载。

如果模板定义在一个源文件,而另一个源文件调用这个模板,就会出错。
因为模板是编译时期时在定义点只编译头部,而这个源文件没有调用点,也就意味着没有函数符号的生成,当其他源文件调用时,通过模板实例化生成了外部符号放在了UND未定义区,但是在链接阶段进行符号解析时,在符号表中却找不到相应的符号,就会出错。

而将模板的定义写在.h,预编译阶段会直接展开.h,在调用时就会有模板的定义。

2.typename和class的区别

在模板类型参数列表中等价,在模板之外,typename可以声明模板中的类型,class作为类标识。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值