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)模板的生成条件

总结:


 


~完

未经作者同意禁止转载

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024年西安交通大学程序设计竞赛校赛

2024年西安交通大学程序设计竞赛校赛 文章目录 2024年西安交通大学程序设计竞赛校赛D瑟莉姆的宴会E: 雪中楼I: 命令行(待补)J&#xff1a;最后一块石头的重量(待补)K: 崩坏&#xff1a;星穹铁道(待补)M&#xff1a;生命游戏N: 圣诞树 D瑟莉姆的宴会 解题思路&#xff1a; ​ …

基于Android Studio 实现的鲜花(购物)商城App--原创

一、高质量源码&#xff08;非开源&#xff09; 关注公众号&#xff1a;《编程乐学》 后台回复&#xff1a;24060201 二、项目演示视频 基于Android Studio 实现的鲜花商城App--原创 三、开发环境 四、设计与实现 1.启动页 启动页我们需要用到倒计时和跳转功能。 2.注册登录 …

Vue中引入elementUI中的container组件失效

1.不用修改官网中任何css或者html 2.按需引入&#xff0c;不是只是引入官网的就可以 import Vue from vue import Router from vue-router import HelloWorld from /components/HelloWorld import First from /components/views/First import Second from /components/views/…

多元联合分布建模 Copula python实例

多元联合分布建模 Copula python实例 目录 库安装 实例可视化代码 库安装 pip install copulas 实例可视化代码 import numpy as np import pandas as pd from copulas.multivariate import GaussianMultivariate# Generate some example data np.random.seed(42) data = …

反向配置教程

注意&#xff0c;Openai、Gemini、claude和pika接口在国内直连不通&#xff0c;都需要配置反向 一、配置openai反向 1、在海外宝塔添加反向 将海外宝塔升级到最新 在海外宝塔添加一个新站点&#xff08;可以解析一个域名来用&#xff0c;也可以用ip端口形式&#xff09; 打开…

大模型应用:Prompt-Engineering优化原则

1.Prompt-Engineering 随着大模型的出现及应用&#xff0c;出现了一门新兴“技术”&#xff0c;该技术被称为Prompt-Enginerring。Prompt Engineering即提示工程&#xff0c;是指在使用大语言模型时&#xff0c;编写高效、准确的Prompt(提示词)的过程。通过不同的表述、细节和…

安全风险 - 组件导出风险

在安全审查中关于组件导出风险是一种常见问题&#xff0c;不同组件都有可能遇到这种问题&#xff0c;而且从一定角度来看的话&#xff0c;如果涉及到三方业务&#xff0c;基本处于无法解决的场景&#xff0c;所以我们需要说明为何无法避免这种风险 组件导出风险能不能规避&…

设计模式(十)结构型模式---享元模式

文章目录 享元模式简介结构UML图具体实现UML图代码实现 享元模式简介 享元模式&#xff08;fly weight pattern&#xff09;主要是通过共享对象来减少系统中对象的数量&#xff0c;其本质就是缓存共享对象&#xff0c;降低内存消耗。享元模式将需要重复使用的对象分为两个状态…

Nginx配置详细解释:(2)events事件配置

在nginx核心配置文件conf/nginx.conf中&#xff0c;有全局配置&#xff0c;events模块&#xff0c;http模块&#xff0c;(http模块中有嵌套多个模块)。常见配置项&#xff0c; events模块中&#xff0c;如下图&#xff1a; events是nginx与用户之间处理事件的功能。 如单个wo…

selenium自动化介绍

文章目录 一、selenium原理 安装二、selenium使用1.创建浏览器对象&#xff0c;访问网址2.消除警告提示3.不显示浏览器中受控制字样4.防检测5.设置延时5.1强制延时5.2隐式延时 6.设置浏览器窗口大小 三、案例实战&#xff1a;百度搜索四、iframe标签五、案例实战&#xff1a;Q…

SpringBoot+百度地图+Mysql实现中国地图可视化

通过SpringBoot百度地图Mysql实现中国地图可视化 一、申请百度地图的ak值 进入百度开发者平台 编辑以下内容 然后申请成功 二、Springboot写一个接口 确保数据库里有数据 文件目录如下 1、配置application.properties文件 #访问端口号 server.port9090 # 数据库连接信息 spr…

【Vue】响应式特性

响应式&#xff1a;简单理解就是数据改变&#xff0c;视图会自动更新。 如何访问 和 修改 data中的数据&#xff08;响应式演示&#xff09; data中的数据, 最终会被添加到实例上 例如这里&#xff0c;app身上就会拥有msg属性&#xff0c;修改msg的值&#xff0c;界面的值也会…

原生APP开发和Flutter开发的比较

原生APP开发和Flutter开发各有优缺点&#xff0c;适用于不同的场景和需求。下面是两者的详细比较&#xff0c;从开发语言、性能、开发效率、维护和更新、社区和支持等多个方面进行分析。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。…

成功解决“IndexError: queue index out of range”错误的全面指南

成功解决“IndexError: queue index out of range”错误的全面指南 引言 在Python编程中&#xff0c;queue模块提供了同步队列类&#xff0c;包括FIFO&#xff08;先进先出&#xff09;队列Queue&#xff0c;LIFO&#xff08;后进先出&#xff09;队列LifoQueue&#xff0c;以…

uniapp登录成功后跳回原有页面+无感刷新token

uniapp登录成功后跳回原有页面 引言 在C端的页面场景中&#xff0c;我们经常会有几种情况到登录页&#xff1a; 区分需要登录和不用登录的页面&#xff0c;点击需要登录才能查看的页面 已经登录但是超时&#xff0c;用户凭证失效等原因 以上情况可以细分为两种&#xff0c;一…

2023年全国青少信息素养大赛智能算法C++挑战赛复赛初中组真题,包含答案解析分享

【读前注意】:此卷是真题,答案解析辛苦整理,大家多多点赞并转发支持,需要下载空白文档题目版本(包含2023年小学组和初中组的题目pdf文件),可以在留言区的第一条留言的链接中进行复制,然后再浏览器中下载即可。 智能算法挑战复赛初中组 (总共

AngularJS基础语法(2009版本)

jquery和AngularJS 数据绑定和获取对比&#xff1a; jquery&#xff0c;要操作DOM&#xff1a; angularJS&#xff0c;无需操作DOM就可以进行动态数据变化&#xff1a; 要使用Angularjs就需要在html页面先引入&#xff1a; ng-app&#xff1a; html页面中&#xff0c;需要给…

操作系统的体系结构:宏内核和微内核

操作系统的体系结构是一个开放的问题。操作系统在核心态为应用程序提供公共的服务&#xff0c;那么操作系统在核心态应该提供什么服务、怎样提供服务&#xff1f;有关这个问题的回答形成了两种主要的体系结构&#xff1a;宏内核和微内核。 宏内核&#xff1a;大而全 宏内核系统…

【面试题-004】ArrayList 和 LinkList区别

ArrayList 和 LinkedList 都是 Java 中常用的动态数组实现&#xff0c;都实现了 List 接口&#xff0c;但它们在内部数据结构和性能方面有所不同&#xff1a; 内部数据结构&#xff1a; ArrayList 是基于动态数组的数据结构&#xff0c;它允许快速随机访问。数组的大小在创建时…

simCSE句子向量表示(1)-使用transformers API

SimCSE SimCSE: Simple Contrastive Learning of Sentence Embeddings. Gao, T., Yao, X., & Chen, D. (2021). SimCSE: Simple Contrastive Learning of Sentence Embeddings. arXiv preprint arXiv:2104.08821. 1、huggingface官网下载模型 官网手动下载&#xff1a;pri…