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.rhkb.cn/news/341555.html

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

相关文章

景源畅信数字:抖音直播人气品类有哪些?

随着短视频平台的兴起&#xff0c;抖音成为了人们日常生活中不可或缺的娱乐方式之一。而抖音直播作为平台的重要组成部分&#xff0c;吸引了大量的观众和主播参与。那么&#xff0c;在抖音直播中&#xff0c;哪些品类能够吸引更多的人气&#xff0c;成为观众们关注的焦点呢?接…

vs2019 c++20 规范 STL库中关于时间的模板 ratio<T,U> , duration<T,U> , time_point<T,U>等

(探讨一)在学习线程的时候&#xff0c;一些函数会让线程等待或睡眠一段时间。函数形参是时间单位&#xff0c;那么在 c 中是如何记录和表示时间的呢&#xff1f;以下给出模板简图&#xff1a; &#xff08;2 探讨二&#xff09;接着给出对模板类 duration_values 的成员函数的测…

python字符串的进阶

在上一篇文章的 密码破解器 中&#xff0c;我们回顾了循环专题的知识点。 while 循环和 for 循环是 Python 中的两大循环语句&#xff0c;它们都可以实现循环的功能&#xff0c;但在具体使用时略有差别。当循环次数不确定时&#xff0c;我们选用 while 循环&#xff1b;当循环…

高并发短视频系统设计:架构、存储与性能优化全解

1. 系统概况与需求分析 1.1 短视频系统简介 当前短视频行业的快速发展&#xff0c;加上用户对高清、流畅观看体验的需求不断提升&#xff0c;对系统的并发处理能力、视频处理速度、存储效率等多方面都提出了极高的要求。那么&#xff0c;我们首先需要了解一个完整的短视频系统…

【C++】list的使用(上)

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a; STL || C 目录 前言&#x1f308;关于list&#x1f525;默认成员函数构造函数&#xff08;constructor&#xff09;析构函数&#xff08;destructor&#xff09;赋值运算符重载 &#x1…

SOCKS 代理 和 HTTP 代理, WebSocket

SOCKS 代理 和 HTTP 代理 的区别 SOCKS 代理 和 HTTP 代理 都是代理服务器&#xff0c;它们充当客户端和目标服务器之间的中介&#xff0c;但它们的工作方式和应用场景有所不同。 1. SOCKS 代理&#xff1a; 工作原理&#xff1a; SOCKS 代理是一种更底层的代理&#xff0c;…

[沫忘录]MySQL InnoDB引擎

[沫忘录]MySQL InnoDB引擎 逻辑存储结构 InnoDB采用 “表、段&#xff0c;区&#xff0c; 页、行” 这样的层级结构进行存储。 **表空间(tablespace)**ibd文件&#xff0c;主要用于存储记录、索引等数据&#xff0c;一个mysql实例可有多个表空间&#xff0c;甚至能通过innodb…

2024年度CCF-阿里云瑶池科研基金正式发布

2024年度CCF-阿里云瑶池科研基金正式发布 截止时间&#xff1a;2024年7月1日24:00&#xff08;北京时间&#xff09; 欢迎CCF会员积极申报 “CCF-阿里云瑶池科研基金”由CCF与阿里云计算有限公司于2024年联合设立&#xff0c;专注于数据库领域&#xff0c;旨在为领域学者提供…

做自媒体素材哪里找?做自媒体必备的几个高质量素材网站分享

在自媒体的世界里&#xff0c;内容是王道。无论是视频还是文章&#xff0c;优秀的自媒体作品都需要有力的内容和高质量的素材作支撑。今天&#xff0c;我为大家整理了一些优质的素材网站&#xff0c;帮助每一位自媒体创作者&#xff0c;无论新手还是老手&#xff0c;都能找到适…

nginx平滑升级

#平滑升级 kill -USR2 <PID号> //查看nginx版本 [rootl1 logs]# nginx -v //查看nginx版本 nginx version: nginx/1.26.0 [rootl1 logs]# 安装一个1.25版本实验一下 [rootl2 ~]# cd /opt/ [rootl2 opt]# [rootl2 opt]# lsnginx-1.25.5.tar.gz nginx-1.26.0.tar.…

Stable Diffusion: Lora篇

前面提到&#xff0c;在提示词中可以使用LoRA并设置权重。LoRA是Low-Rank Adaptation的简写&#xff0c;直译为轻量级微调&#xff0c;是一种通用的AI大模型微调技术&#xff0c;通过LoRA使用可以对Stable Diffusion模型输出进行微调型&#xff0c;更加随心所欲地实现定制华输出…

数据结构--双向链表

目录 一.链表的分类 二.双向链表的结构 三.双向链表的实现 1.初始化 2.尾插与头插 3.尾删与头删 4.在指定位置之后插入数据 查找函数 5.删除指定节点 6&#xff0c;销毁链表 四.完整代码 List.h List.c 一.链表的分类 链表的结构⾮常多样&#xff0c;以下情况组合起…

单列集合--ArryList、LinkedList、Set

使用IDEA进入某个类之后&#xff0c;按ctrlF12,或者alt数字7&#xff0c;可查看该实现类的大纲。 package exercise;import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.function.Consumer;public class Demo3 {public static void…

ShowDoc item_id 未授权SQL注入漏洞复现

0x01 产品简介 ShowDoc 是一个开源的在线文档协作平台,它支持Markdown、图片等多种格式,方便团队成员共同编辑和分享文档。企业常见使用场景是使用其进行接口文档、内部知识库管理。 0x02 漏洞概述 2024年6月,ShowDoc官方发布新版本修复了一个SQL注入漏洞。鉴于该漏洞无前…

基于三元组一致性学习的单目内窥镜里程计估计

文章目录 TCL: Triplet Consistent Learning for Odometry Estimation of Monocular Endoscope摘要方法实验结果 TCL: Triplet Consistent Learning for Odometry Estimation of Monocular Endoscope 摘要 单目图像中深度和姿态的估计对于计算机辅助导航至关重要。由于很难获…

6. MySQL 查询、去重、别名

文章目录 【 1. 数据表查询 SELECT 】1.1 查询表中所有字段使用 * 查询表的所有字段列出表的所有字段 1.2 查询表中指定的字段 【 2. 去重 DISTINCT 】【 3. 设置别名 AS 】3.1 为表指定别名3.2 为字段指定别名 【 5. 限制查询结果的条数 LIMIT 】5.1 指定初始位置5.2 不指定初…

映射网络驱动器自动断开的解决方法

如果将驱动器映射到网络共享&#xff0c;映射的驱动器可能会在定期处于非活动状态后断开连接&#xff0c;并且 Windows 资源管理器可能会在映射驱动器的图标上显示红色 X。&#xff0c;出现此行为的原因是&#xff0c;系统可以在指定的超时期限后断开空闲连接&#xff0c; (默认…

【Kubernetes】k8s的调度约束(亲和与反亲和)

一、调度约束 list-watch 组件 Kubernetes 是通过 List-Watch 的机制进行每个组件的协作&#xff0c;保持数据同步的&#xff0c;每个组件之间的设计实现了解耦。 用户是通过 kubectl 根据配置文件&#xff0c;向 APIServer 发送命令&#xff0c;在 Node 节点上面建立 Pod 和…

0基础学习Elasticsearch-使用Java操作ES

文章目录 1 背景2 前言3 Java如何操作ES3.1 引入依赖3.2 依赖介绍3.3 隐藏依赖3.4 初始化客户端&#xff08;获取ES连接&#xff09;3.5 发送请求给ES 1 背景 上篇学习了0基础学习Elasticsearch-Quick start&#xff0c;随后本篇研究如何使用Java操作ES 2 前言 建议通篇阅读再回…

c语言项目-贪吃蛇项目2-游戏的设计与分析

文章目录 前言游戏的设计与分析地图&#xff1a;这里简述一下c语言的国际化特性相关的知识<locale.h> 本地化头文件类项setlocale函数 上面我们讲到需要打印★&#xff0c;●&#xff0c;□三个宽字符找到这三个字符打印的方式有两种&#xff1a; 控制台屏幕的长宽特性&a…