文章目录
- 模板的作用
- 模板的原理
- 模板分为两大类——函数模板和类模板
- 函数模板
- 语法
- 函数模板实例化模板函数的方式
- 模板函数的类型转换
- 既有函数模板又有已经实现的函数,会优先调用哪一个?
- 类模板
- 语法
- 模板类实例化对象
- 模板类的模板参数可以有缺省值
- 类模板中的成员函数全函数模板
- 模板类中的成员函数声明和定义分离
模板的作用
模板的主要作用是实现泛型编程
,泛型编程即编写与类型无关的通用代码,是代码复用的一种手段
模板就是泛型编程的基础。
例
我们经常使用的交换函数就可以使用泛型编程来进行编写,这样可以大大减少重复的代码
一般编写方式
可以发现上图的三个函数除了参数类型不一样,其他的都是一样的重复的代码太多了,这个时候就可以考虑使用模板进行编写了
模板编写方式:
此时编译器就可以根据类型的不同实例化出不同的函数
模板的原理
模板就如其名字一样,就像一个冰棍模具,把不同的果汁(类型)放进去就可以得到不同口味的冰棍,但是这些冰棍就只有口味不同,外形(代码逻辑)都是一样的。
如下图
在编译器编译阶段
,对于模板的使用,编译器会根据传入的实参类型来推演生成对应类型的函数以供
调用。
模板分为两大类——函数模板和类模板
函数模板
语法
template < typename/class T(自定义类型名), typename/class T,………>
函数模板定义
例
函数模板实例化模板函数的方式
-
只传实参(隐式调用)[让编译器自己根据实参类型推]
-
既传实参又传类型(显示调用)[即自己指定模板使用的类型]
必须显式传类型
的场景
即仅靠实参推演出的类型不够
例
此时就必须显式传类型
当模板类型个数和实参个数不同时,可能会类型不明
例
此时有3个解决方法
-
对实参进行强制类型转换
即 把函数调用方式改为:Add(a,(int)c)
或者Add((char) a,c)
,让参数类型统一。 -
显式传递模板参数
即 把函数调用方式改为Add<int>(a,c)
或者Add<char>(a,c)
,让编译器知道模板参数是什么,先实例化出对应的函数之后,再传入实参,此时实参就会自动进行隐式类型转换 -
增加模板参数个数到于实参个数匹配
即
模板函数的类型转换
使用实参类型推导模板参数时(隐式调用),不能
隐式类型转换
显式调用时可以
隐式类型转换
既有函数模板又有已经实现的函数,会优先调用哪一个?
例如下图这种情况:
此时分3种情况
-
如果调用普通函数
不会发生类型转换
,那就调用已经存在的普通函数
-
如果调用普通函数
会
发生类型转换,而调用函数模板实例化的模板函数不会发生类型转换
时,就调用模板函数
-
如果都
会
发生类型转换,就调用已经存在的普通函数
类模板
语法
template < typename/class T(自定义类型名), typename/class T,………>
模板类的定义
例
模板类实例化对象
模板类实例化对象时只能显式传类型
此时<>加在类名后面
[<>必须要有,哪怕<>中不写类型]
例
模板类的模板参数可以有缺省值
例
类模板中的成员函数全函数模板
为什么?
因为类模板中的成员变量的类型是不确定的,成员函数是否使用了类模板的模板类型不确定
所以他没有办法像普通类的成员函数一样,在编译时就创建好了
而是跟函数模板一样需要编译器在运行过程中推演
模板类中的成员函数声明和定义分离
不能简单地直接类名+::
而是
模板类型声明+类中的成员函数的定义
[注意此时类名后面还是要加<类型>,因为这样才是一个完整的类类型],这样类实例化对象的时候就可以推导出对应的成员函数的类型
由于模板类不完整,所以不能直接用它去限定作用域
即在类外实现成员函数等用::区限定时,不能直接限定,要先指定类型