C++ : 模板初阶

标题:C++ : 模板初阶

@水墨不写bug


35c30ce09f2e4cfcb18d1a24af689db1.png


正文开始:

 

C语言的问题 :

写不完的swap函数

        在学习C语言时,我们有一个经常使用的函数swap函数,它可以将两个对象的值交换。

我们通常这样实现它:


void swap(int t1,int t2)
{int tem = t1;t1 = t2;t2 = tem;
}

         这是很简单的函数。如果我们想交换其他类型的对象,就需要用到函数重载


void swap(int t1,int t2){int tem = t1;t1 = t2;t2 = tem;}
void swap(float t1, float t2){ float tem = t1;t1 = t2;t2 = tem;}
void swap(double t1, double t2){ double tem = t1;t1 = t2;t2 = tem;}
void swap(char t1, char t2){ char tem = t1;t1 = t2;t2 = tem;}
void swap(long t1, long t2){ long tem = t1;t1 = t2;t2 = tem;}
//...

         但是,内置类型的指针理论上可以达到n级指针,n可以趋于无限大;除此之外,还有自定义类型。这样一来,你就会发现,类型是无法枚举的!一个swap函数需要实现一种类型的交换,那么swap函数是写不完的!


        人工解决重复写同一个逻辑,仅仅是类型不同的代码是很低效的!为了解决这个问题,C++引入模板的概念:

        顾名思义,模板就是模板,作用就是印出不同的东西,这个东西就是swap函数!


 

(一)模板简介

         模板是C++相对于C的一个新的语法,他需要用到关键字template(模板),如果我们用模板来实现swap函数,就可这样写:


template <typename T>
void swap(T& t1, T& t2)
{T tem = t1;t1 = t2;t2 = tem;
}

        template<typename或class 模板参数> + 模板主体 就是模板的基本形式。

        要成功的调用模板函数,也需要特定的条件:


template <class T>
void swap(T& t1, T& t2)
{T tem = t1;t1 = t2;t2 = tem;
}
/*void swap(int t1,int t2){int tem = t1;t1 = t2;t2 = tem;}void swap(float t1, float t2){ float tem = t1;t1 = t2;t2 = tem;}void swap(double t1, double t2){ double tem = t1;t1 = t2;t2 = tem;}void swap(char t1, char t2){ char tem = t1;t1 = t2;t2 = tem;}void swap(long t1, long t2){ long tem = t1;t1 = t2;t2 = tem;}
*/
int main()
{int a = 1, b = 5;::swap(a, b);cout << a << " " << b << endl;return 0;
}

         我们可以指定调用全局的swap模板函数,完成对象的值交换。

(1)为什么模板可行?模板的原理?

        在编译时,编译器会 匹配 模板参数的类型,如上例,a,b的类型都是整形,所以编译器会根据根据函数的模板生成一份函数,这份函数仅仅将 T模板参数 改成 匹配出的类型):

void swap(int t1,int t2)
{int tem = t1;t1 = t2;t2 = tem;
}

        这样我们人工编写的工作量就减少了非常多,因为一个理论上模板可以生成n个函数(n可趋于无穷大)。

(2)匹配冲突

         由于交换的是两个参数的值,当两个参数的类型不同时,理论上不能交换,此时编译器也会因为匹配类型冲突而报错:

5385c38ab2074763a4c3c8361d4bbddc.png

        如何解决这个问题:

i,再增加一个模板参数(推荐)

        由于只有一个模板参数, 当推断类型既是int又是double时,一个参数就应付不过来了,所以需要再增加一个参数。两个模板参数,就可以进行两次参数类型匹配:


template <class T1,class T2>
void swap(T1& t1, T2& t2)
{auto tem = t1;t1 = t2;t2 = tem;
}

 

        ##如果你是看了后面两种处理方式后又回来的,那么你很敏锐,发现了本例的问题,其实本例也发生了类型转换

        由于t1,t2类型不一致,所以在下面赋值时,必然发生了类型转换,可能导致精度丢失。但是这并不影响我们解决匹配冲突的方法:因为在实际应用中,我们一般不会让模板参数之间进行运算。(这仅仅是本例的场景不太好,使用多个模板来解决匹配冲突仍然是推荐的) ##

 


ii,强制转换到一致

        只能解决一些特例,就拿下面这个函数模板来说:


template<class T>
T Add(T t1,T t2)
{return t1 + t2;
}void test2()
{int a = 1;double b = 5.6;double ret = ::Add((double)a, b);cout << ret;
}

         将a强制类型转换到double可以使编译器对a和b类型匹配都是double,这样是可以通过编译的,但是这样操作的结果是不稳定的。强制类型转换必然意味着精度或者符号的丢失,不到迫不得已,不要使用强制类型转换!

iii,显示实例化(不再推演参数)

         只能解决一些特例,还是拿上面这个函数模板来说:


template<class T>
T Add(T t1,T t2)
{return t1 + t2;
}
void test2()
{int a = 1;double b = 5.6;double ret = ::Add<double>(a, b);cout << ret;
}

        这也是可以通过编译的,在模板名称后面加上<参数类型>,意味着指定了生成的模板的匹配类型就是 这个指定的参数类型。由于指定了参数类型,其本质也是发生了类型转换,而类型转换是不推荐的。

总结:

        解决匹配冲突的方法:

                 i,再增加一个模板参数(推荐)

                ii,强制转换到一致

                iii,显示实例化(不再推演参数)


(3)模板实例化的条件

        当全局已经有一个匹配的函数时,模板就不会再生成新函数。

        以这个例子来说: 


void swap(double t1, double t2){ double tem = t1;t1 = t2;t2 = tem;}template <class T>
void swap(T& t1, T& t2)
{T tem = t1;t1 = t2;t2 = tem;
}
void test1()
{double a = 1;double b = 5;::swap(a, b);cout << a << " " << b << endl;
}

        模板匹配的类型是double,由于全局已经有了类型是double的函数,所以编译器不会再通过函数模板再生成一份swap函数。

        编译器会优先匹配最合适的模板:


template <class T1,class T2>
void swap(T1& t1, T2& t2)
{T1 tem = t1;t1 = t2;t2 = tem;
}
template <class T>
void swap(T& t1, T& t2)
{T tem = t1;t1 = t2;t2 = tem;
}
void test1()
{int a = 1;double b = 5;::swap(a, b);cout << a << " " << b << endl;
}

        本例中,由于a,b两个变量的类型不同,所以编译器会优先选择两个模板参数的swap函数,而不是一个模板参数的swap。


        在特殊情况也是可以编译通过的:


void swap(int t1, int t2)
{ int tem = t1; t1 = t2; t2 = tem; 
}
void test1()
{int a = 1;double b = 5;::swap(a, b);cout << a << " " << b << endl;
}

总结:

        1.全局有完全匹配的函数,编译器不再由模板生成函数;直接使用现成的函数。

        2.如果有多个模板,编译器会选择最合适的模板来生成函数;

        3.类型不匹配,没有模板,也可以编译通过,但是会发生类型转换,导致精度丢失。

        其实模板远不止这些,本文仅仅是以函数模板为引子来讲解模板的语法,而模板还包括类模板,后者是实际中应用较多的。

        类模板 就放 在将来为大家分享吧! 

 


目录

(一)模板简介

(1)为什么模板可行?模板的原理?

(2)匹配冲突

i,再增加一个模板参数(推荐)

ii,强制转换到一致

iii,显示实例化(不再推演参数)

总结:

(3)模板的生成条件

总结:


 


~完

未经作者同意禁止转载

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/21049.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【vue实战项目】通用管理系统:作业列表

目录 目录 1.前言 2.后端API 3.前端API 4.组件 5.分页 6.封装组件 1.前言 本文是博主前端Vue实战系列中的一篇文章&#xff0c;本系列将会带大家一起从0开始一步步完整的做完一个小项目&#xff0c;让你找到Vue实战的技巧和感觉。 专栏地址&#xff1a; https://blog…

300价值与300成长指数

300价值指数&#xff0c;看的是价值因子。价值因子包含四个变量:股息收益率&#xff0c;每股净资产与价格比率&#xff0c;每股净现金流与价格比率&#xff0c;每股收益与价格比率 根据这四个变量对沪深300指数的成份股进行打分&#xff0c;得分高的前100名&#xff0c;就纳入…

安卓Launcher启动详解

目录 一、概述1.1 核心源码1.2 主要代码作用 二、源码分析2.1 第一阶段SystemServer 启动HomeActivity的调用阶段2.2 [RootActivityContainer.java] startHomeOnDisplay()2.2.1 [ActivityTaskManagerService.java] getHomeIntent()2.2.2 [RootActivityContainer.java] resolveH…

uni-app解决表格uni-table样式问题

一、如何让表格文字只显示一行&#xff0c;超出部分用省略号表示 步骤 &#xff1a; 给table设置table-layout:fixed; 列宽由表格宽度和列宽度设定。&#xff08;默认是由单元格内容设定&#xff09;让表格元素继承父元素宽度固定table-layout: inherit;overflow: hidden;超过…

list的简单模拟实现

文章目录 目录 文章目录 前言 一、使用list时的注意事项 1.list不支持std库中的sort排序 2.去重操作 3.splice拼接 二、list的接口实现 1.源码中的节点 2.源码中的构造函数 3.哨兵位头节点 4.尾插和头插 5.迭代器* 5.1 迭代器中的operator和-- 5.2其他迭代器中的接口 5.3迭代器…

【气象常用】剖面图

效果图&#xff1a; 主要步骤&#xff1a; 1. 数据准备&#xff1a;我用的era5的散度数据&#xff08;大家替换为自己的就好啦&#xff0c;era5数据下载方法可以看这里【数据下载】ERA5 各高度层月平均数据下载_era5月平均数据-CSDN博客&#xff09; 2. 数据处理&#xff1a…

windows10系统64位安装delphiXE11.2完整教程

windows10系统64位安装delphiXE11.2完整教程 https://altd.embarcadero.com/download/radstudio/11.0/radstudio_11_106491a.iso XE11.1 https://altd.embarcadero.com/download/radstudio/11.0/RADStudio_11_2_10937a.iso XE11.2 关键使用文件在以下内容&#xff1a;windows10…

Java Spring Boot 从必应爬取图片

获取图片主要就是通过必应图片页面控制台的元素&#xff0c;确认图片和标题在哪个类中&#xff08;浏览器 F12&#xff09; 引入依赖 这里需要引入两个依赖 jsoup 和 hutool maven依赖网站地址&#xff1a;Maven Repository: Search/Browse/Explore (mvnrepository.com) 挑选…

极简网络用户手册(1)

极简网络系统处理流程 模块位置&#xff1a;参数平台--专题分析--极简网络分析 步骤&#xff1a; 步骤一&#xff1a;创建精细化场景策略 步骤二&#xff1a;创建任务&#xff0c;主要选择策略&#xff08;包括√配置和距离配置&#xff09;和需要处理的小区清单&#xff08;源…

srcu浅析

本文代码基于linux内核4.19.195 之前写了rcu、rcu nocb的文章&#xff0c;感觉还差个srcu就完整了&#xff0c;现在补齐一下。 SRCU&#xff08;Sleepable RCU&#xff09;是rcu的一个变体&#xff0c;顾名思义&#xff0c;就是在rcu的读临界区中允许睡眠&#xff0c;而rcu在读…

CCF-GESP 等级考试 2023年6月认证C++四级真题解析

2023年09月真题 一、单选题&#xff08;每题2分&#xff0c;共30分&#xff09; 第 1 题 高级语言编写的程序需要经过以下&#xff08; &#xff09;操作&#xff0c;可以生成在计算机上运行的可执行代码。 A. 编辑B. 保存C. 调试D. 编译 答案&#xff1a;D 解析&#xff1a;…

FS212E 系列PD协议

PD快充协议芯片FS212EL、FS212EH可以智能的识别插入的手机类型&#xff0c;选择最为合适的协议应对手机快充需要。兼容多类USB Type-C协议&#xff0c;包括TypeC协议、TypeC PD2.0、TypeC PD3.0、TypeC PD3.2等协议。集成OPTO输出&#xff0c;通过电阻直驱反馈光耦。FS212E 的调…

C++函数模版和C#的泛型函数的区别

函数模板和C#的泛型函数在概念上是非常相似的&#xff0c;都是用于创建能够处理多种数据类型的通用函数。它们的主要区别点在于语法和实现细节上&#xff1a; 语法&#xff1a; C 中的函数模板使用 template <typename T> 或者 template <class T> 来声明模板&am…

新手学习STM32还是ESP32

对于新手来说&#xff0c;选择学习STM32还是ESP32取决于个人的学习目标和背景。以下是针对这两种微控制器的详细分析&#xff0c;以便您做出更明智的选择&#xff1a; STM32 1. 处理器架构与性能 STM32采用单核或多核处理器架构&#xff0c;基于ARM Cortex-M0&#xff0c;M0…

PlantUML-使用文本来画时序图

介绍 PlantUML 是一个开源工具&#xff0c;用户可以使用纯文本描述来创建 UML (统一建模语言) 图形。由于它使用文本来描述图形&#xff0c;因此可以很容易地将这些描述与源代码一起存储在版本控制系统中。然后&#xff0c;PlantUML 负责将这些描述转换为图形。 资料 官方文…

k8s部署1.27.3版本,使用containerd模式

环境 操作系统&#xff1a;centos7.9 机器&#xff1a;1个master 和 1个node 节点&#xff08;云ECS&#xff09; 系统设置 # 所有机器设置hostname hostnamectl set-hostname master1 hostnamectl set-hostname node1# 所有机器增加内网ip和 master1 对应关系 vi /etc/ho…

杂牌记录仪TS视频流恢复方法

大多数的记录仪都采用了MP4/MOV文件方案&#xff0c;极少数的可能在用AVI文件&#xff0c;极极少数的在用TS文件方案。很多人可能不太解TS文件&#xff0c;这是一种古老的视频文件结构&#xff0c;下边这个案例我们来看下TS视频文件的恢复方法。 故障存储:8G存储卡/fat32文件系…

2-1RT-Thread线程管理-笔记

2-1RT-Thread线程管理-笔记 其中系统线程由内核创建&#xff0c;如main函数和空闲线程都属于系统线程&#xff0c;而用户线程是由应用程序所创建的。 对于资源较大的MCU可以适当设计较大的线程栈&#xff0c;也可以在初始化时设置一个具体的数值&#xff0c;如1K或2K字节。…

索引 ---- mysql

目录 1. 索引 1.1 概念 1.2 作用 1.3 使用场景 1.4 使用 1.4.1查看索引 1.4.2 创建索引 1.4.3 删除索引 1.5 注意事项 1.6 索引底层的数据结构 (面试经典问题) 1. 索引 1.1 概念 索引是一种特殊的文件&#xff0c;包含着对数据表里所有记录的引用指针。可以对表中的…

2006NOIP普及组真题 2. 开心的金明

线上OJ&#xff1a; 【06NOIP普及组】开心的金明 本题只要把 1、限定金额看成背包总容量 2、每件物品的价格 v 看成占用背包的体积 3、每件物品的价格v乘以权重w作为该物品的价值 则本题可套用标准的01背包问题模板&#xff1a; f [ j ] m a x ( f [ j ] , f [ j − v ] w …