稍微深入函数模板
发布日期:2022-02-07 06:39:45
浏览次数:6
分类:技术文章
本文共 3504 字,大约阅读时间需要 11 分钟。
C++中的函数新特性(二)
- 浅谈函数模板和泛型编程
- 函数模板的实现原理
- 扩展:模板重载简介
- 局限性:模板的具体化
- 局限性:模板实例的选择
- 函数的选择
- 局限性:类型的判定
函数模板
浅谈函数模板与泛型编程
泛型编程
通过一种语言机制 实现一个通用的方法(标准容器库) 通用是指:每一种数据类型都能用
函数模板怎么实现泛型编程呢?
上次的例子:
如果我要打一个交换两个int变量的函数 我可以这样void swap(int & a, int & b){ int temp; temp = a; a = b; b = temp;}
如果通过函数模板来实现泛型编程
则可以这样templatevoid swap(T & a, T & b){ T temp; temp = a; a = b; b = temp;}
关键字:template(模板)
关键字:typename
第一行,指出这是一个函数模板
一个可替代的变量类型,我们暂且命名为T 然后后面函数的定义 就用T来代替具体的类型 之后我们的函数调用 就可以用这一个swap函数,去交换int变量 去交换double变量实际上函数模板就是在编译的时候
将T换成所需要的类型 就像 typedef T int ;一样
所以我们在调用这个函数的时候
可以这样调用double a,b;swap(a,b);int c,d;swap(c,d);
然后就达到了泛型编程的目的
对,无论什么类型,都可以调用 吗? 。 。 。 当然,事情并没有那么简单。 数据类型,远远不止这几种基本类型 还有结构体,还有类 还有…………………… 这个在后面的局限性里拓展一下函数模板的实现原理
templatevoid swap(T & a, T & b){ T temp; temp = a; a = b; b = temp;}
函数模板的语法
template是模板的关键字 typename是在模板中说明类型的关键字template < typename T >
在第一行中,先声明了一个类型参数T 然后在函数的具体定义中, 把需要用到该类型的全部用T 原理:
在编译过程 编译器会自动根据函数的调用情况 知道需要使用哪种类型的函数 然后就会 直接把T替换成对应的类型 生成一个新函数
所以,在编译过程,
模板就会变成真正的函数 (这个过程叫做模板的实例化) 在编译过后,程序就没有模板了 只有对应的函数模板的重载
同函数一样,模板也是可以重载的
(还记得什么叫函数的重载吗?) (C++中允许同名函数的存在) (虽然同名,不过会根据函数的特征标选择) (所以不会产生冲突)模板重载
同名函数模板 在C++中是允许存在的 前提是模板的参数不同
先放个例子
templateT add(T a, T b){ return a+b;}
对这个模板,
我们可以使两个相同类型的变量相加 然后返回他们的和templateT1 add(T1 a, T2 b){ return a+b;}
对这个模板
我们可以使两个具有不同类型的变量相加 并且返回第一个变量所具有的类型 当然,我们还可以这样templateT add(T a, T b){ return a+b+t;}
而这个模板,我们可以在模板参数里
放入一个int类型的整数 然后相加的时候加多一个数模板局限性1:
如果模板对某一种类型不合适
可以自己另外定义一个函数代替模板吗
假设我现在有这样一个模板
templatevoid swap(T & a, T & b){ T temp; temp = a; a = b; b = temp;}
然后我有这样一种
用结构体定义的数据类型struct person{ char name[40]; int age;};
我能否这样?
person p1,p2;// after initializationswap( p1, p2 );
虽然C++允许结构体的赋值
可是,如果我只想交换age这一个变量呢 只想交换年龄而不改名可以吗?模板的显式具体化技术
我们想做的事情就是
想让一个模板,对其余的所有类型都适用 唯独用person类型的时候 我想另外定义一个而不使用模板的定义 这里,我们使用模板的显式具体化技术具体化:
就是对某一种类型给出具体的定义
这里我先给出person类型的显式具体化例子
//函数原型template <> void swap(person & a, person & b);//函数定义template <> void swap (person & a, person & b){ int temp; temp = a.age; a.age = b.age; b.age = temp;}
对比普通模板
//函数原型templatevoid swap(T & a, T & b);//函数定义template void swap(T & a, T & b){ T temp; temp = a; a = b; b = temp;}
区别1:template还在
但是后面的<>中可自定义的类型参数没有了 也就是说没有了可自定义的类型参数 区别2:函数名 函数名后面加上了< person > 表明这是该模板下该类型的定义 区别3:形参列表及函数定义 没有了自定义的类型参数 后面的一切操作 都是由具体的类型完成函数的具体化
使得在调用函数的时候 能够根据类型,选择使用模板函数 还是用模板函数下的具体化
模板局限性2:
假设有这样一种模板函数
templateT add(T a, T b){ return a+b;}
问题描述
使这个模板函数这样调用int a = 2;double d = 3.0;add(a,d);模板函数该如何选择类型?
这里我们需要用到模板的另一种语法
模板的实例化技术
什么是实例化?
就是从模板变成真正的函数
实例化分为 隐式实例化和显式实例化
隐式实例化
在前面的原理中我们说到
模板在编译过程就会变成真正的函数 在编译后 代码是没有模板这一种东东的 模板,已经变成了一个一个的函数 如果以上面的swap函数为例 我一旦调用swap函数int a = 2;int b = 3;add(a,b);
编译器便会知道我需要int类型的add函数
然后就会帮我通过模板 生成int类型函数int add(int a, int b){ return a+b;}
这样,就是函数模板的一个实例化
由于我并没有告诉编译器要实例化 只是编译器比较聪明知道应该要这么做 就叫隐式实例化显式实例化
如果要明确的告诉编译器
需要实例化某一个类型的函数 我可以这样做template void add (int , int );
通过这一条语句
编译器便会知道需要生成int类型的swap函数 (无论我后面有没有调用)还可以通过这样的方法
在函数调用的时候调用显式实例化的一个函数int x = 3;double y = 3.2;double sum = add(x,y);
从而解决模板函数调用模糊不清
不知道该调用谁的问题提出几个问题
- 所有类型都可以直接相加吗? 如:我用前面的add函数模板,去相加字符串可以吗?
其实是不可以的
但是如果可以自己为字符串重载运算符+ (也就是自己定义一个适用于字符串的+运算符) 也许就可以了
- 如果我在模板中不知道该返回什么类型的参数
template???? add(T1 a, T2 b){ return a+b;}
上面的程序,我可不能确定a+b的类型呀
可能是T1,也可能是T2 这可不能随便定下来呀 能不能在运行的时候根据我的参数的输入决定呢?这个又能扯到很多东西……下次再说说
我还是希望能够快点讲到类……
这样又有一堆东西可以写了……
转载地址:https://blog.csdn.net/wyfwyf12321/article/details/54669487 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
逛到本站,mark一下
[***.202.152.39]2024年04月18日 11时00分37秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
cocos2dx-lua在ios上实现生成及扫描二维码
2019-04-27
GoLang初探
2019-04-27
golang Leaf 游戏服务器框架简介
2019-04-27
MySQL数据库视图:视图定义、创建视图、修改视图
2019-04-27
以太坊轻钱包MetaMask详细图文教程
2019-04-27
第一章 logback简介
2021-06-30
第二章:Logback架构
2021-06-30
第三章:logback 的配置
2021-06-30
Logback第四章:Appenders
2021-06-30
Logback第五章:Encoder
2021-06-30
Logback第六章:Layouts
2021-06-30
Logback第七章:Filters
2021-06-30
Logback第八章:MDC(Mapped Diagnostic Context)
2021-06-30
Logback第九章:日志隔离
2021-06-30
logback-中文手册
2021-06-30
Logback第十章:JMX 配置器
2021-06-30
Logback 第十一章:Joran
2021-06-30
Logback第十二章:Groovy 配置
2021-06-30
Logback第十三章:从 log4j 迁移
2021-06-30
Logback第十四章:Receivers
2021-06-30