C++:模板(2)

 

目录

非类型模板参数

模板的特化

概念

函数模板特化

类模板特化

全特化

偏特化

模板的分离编译

分离编译的概念

模板的分离编译

​编辑

模板总结


非类型模板参数

模板参数分为类型形参与非类型形参。

类型形参:在模板参数列表中,跟在class或者typename之类的参数类型名称。

template<class T>
void swap(T& x, T& y)
{T tmp = x;x = y;y = tmp;
}

非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量使用。

template<class T, size_t N = 30>
class A
{
private:T _array[N];
};int main()
{A<int> a1; // N = 30A<int, 10> a2; // N = 10;return 0;
}

注意

1.非类型模板参数给的常量的缺省值只能是整型常量不能是浮点数、类对象和字符串等。

2.非类型模板参数必须在编译期就能确认结果。

以下用法是错误的,因为N不确定。

模板的特化

概念

通常情况下,模板可以实现一些与类型无关的代码,但对于一些特殊类型(比如指针)可能会得到一些错误的结果。

就比如说一个小于的比较函数模板。

​
template<class T>
bool Less(T x, T y)
{return x < y;
}​

对于普通的int、double等类型,这个函数可以得到正确结果。

cout << boolalpha << Less(3, 4) << endl;
cout << boolalpha << Less(1.0, 5.0) << endl;

但如果对于指针类型,那结果就有问题了。

int a = 8;
int b = 4;
cout << boolalpha << Less(&a, &b) << endl;

很明显,这个结果是错的(正确结果应该是false),因为这个Less函数比较的是a、b的地址,没有比较指针指向的内容。

为了解决这种问题,就引入了模板特化了。

模板特化:在原模板的基础上,针对特殊类型进行特殊化的实现方式。

模板特化分为函数模板特化与类模板特化。

函数模板特化

函数模板特化的条件

1.一个基础的函数模板。

2.关键字template后面接空<>。

3.函数名后跟<>,<>里指定特化的类型。

4.函数形参表必须要和模板函数的基础类型参数完全相同,不同的话,编译器会报错。

template<class T>
bool Less(T x, T y)
{return x < y;
}//对Less函数模板进行特化
template<>
bool Less<int*>(int* pa, int* pb)
{return *pa < *pb;
}

进行特化后上面的例子就得到了正确答案了。

int a = 8;
int b = 4;
cout << boolalpha << Less(&a, &b) << endl;

但是一般情况下函数模板遇到不能处理或者处理有误的类型时,直接将该类型的函数给出。

bool Less(int* pa, int* pb)
{return *pa < *pb;
}

这种实现简单明了,代码可读性高,而对于一些参数类型复杂的函数模板,特化时比较麻烦,因此函数模板不建议特化。

类模板特化

类模板特化也分全特化和偏特化。

全特化

全特化就是将模板参数列表所有参数都确定化。

template<class T1, class T2>
class Data
{
public:Data(){cout << "Data<T1, T2>" << endl;}
private:T1 _x;T2 _y;
};template<>
class Data<int, char>
{
public:Data(){cout << "Data<int, char>" << endl;}
private:int _x;char _y;
};

全特化就是将T1和T2确定为int和char。

//匹配原始类模板
Data<int, double> D1;//匹配特化的类模板
Data<int, char> D2;

偏特化

有两种表现表现方式

部分特化:将模板参数列表中的一部分参数特化

template<class T1, class T2>
class Data
{
public:Data(){cout << "Data<T1, T2>" << endl;}
private:T1 _x;T2 _y;
};template<class T1>
class Data<T1, int>
{
public:Data(){coutv << "Data<T1, int>" << endl;}
private:T1 _x;int _y;
};

将第二个参数T2特化成int,只要第二个参数类型是int就匹配特化版本。

//匹配原始类模板
Data<int, double> D1;
Data<double,char> D2;
cout << endl;//匹配部分特化的类模板
Data<int, int> D3;
Data<char*, int> D4;
Data<double, int> D5;

参数限制:针对模板参数更进一步的条件限制设计出来的一个特化版。

template<class T>
class Less
{
public:bool operator()(const T& x, const T& y){return x < y;}
};//偏特化,对参数类型做出进一步的限制
template<class T>
class Less<T*>
{
public:bool operator()(const T* pa, const T* pb){return *pa < *pb;}
};

这里进行了一个偏特化,将模板参数T限制为T*,这样在传入指针类型时就会匹配偏特化版本,对指针指向的内容进行比较,而不是存储的地址。

运行例子

int a = 3;
int b = 10;cout << boolalpha << Less<int>()(a, b) << endl;//匹配偏特化
cout << boolalpha << Less<int*>()(&a, &b) << endl;

模板的分离编译

分离编译的概念

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式

模板的分离编译

对于一般的分离编译,我们可以将函数的声明和定义分离,但是对于模板,我们将函数的声明和定义分离会导致链接错误。

原因如下:

func.h 函数声明

func.cpp 函数定义

test.cpp 调用函数

报链接错误的直接原因就是链接时,符号表没有对应函数的地址。

1.代码开始编译的时候,首先就预处理,把头文件展开、宏替换、条件编译、去掉注释,.h和对应对的.cpp文件合在一起生成.i文件;

2.然后就到编译,根据语法树,检查语法,生成对应对的汇编代码,模板这时候问题就出在这,函数的.i文件,有声明有定义,没有具体类型,test.i中有函数的声明,有类型,但是没有定义,所以就不能生成具体的函数符号表也就没有对应的地址,函数.i文件普通函数有声明有定义有类型,可以生成,这时test.i还是转换成汇编 call func(?),等着链接时把地址连接上,也没有报错,由.i文件生成.s文件;

3.编译完就到了汇编,汇编代码转换成二进制机械码,生成.obj文件;

4.链接时把目标文件合并在一起生成可执行程序,并把需要的函数地址等连接上。

解决方法:声明和定义不分离(推荐);模板定义的位置显式实例化。

模板总结

模板的优点

1.代码可以复用,节省资源,提高效率,便于更快迭代开发,C++标准模板(STL)因此而生。

2.代码更灵活

模板的缺点:

1.代码膨胀,编译时间变长。

2.模板出现错误时,信息容易错乱,不利于排查。


拜拜,下期再见😏

摸鱼ing😴✨🎞

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

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

相关文章

HttpURLConnection构造请求体传文件

HttpURLConnection构造请求体传文件 在Java中&#xff0c;使用HttpURLConnection构造请求体传输文件&#xff0c;你需要做以下几步&#xff1a; 1、创建URL对象指向你想要请求的资源。 2、通过URL打开连接&#xff0c;转换为HttpURLConnection实例。 3、设置请求方法为POST。 …

GB/T28181-2022规范解读、应用场景和技术实现探究

GB/T28181-2022和GB/T28181-2016区别 GB/T28181-2022《公共安全视频监控联网系统信息传输、交换、控制技术要求》与 GB/T28181-2016 相比&#xff0c;主要有以下区别&#xff1a; 术语和定义方面&#xff1a; 术语删减&#xff1a;GB/T28181-2022 删除了 “联网系统信息”“数…

Win安装Redis

目录 1、下载 2、解压文件并修改名称 3、前台简单启动 4、将redis设置成服务后台启动 5、命令启停redis 6、配置文件设置 1、下载 【下载地址】 2、解压文件并修改名称 3、前台简单启动 redis-server.exe redis.windows.conf 4、将redis设置成服务后台启动 redis-server -…

pikachu靶场CSRF-post测试报告

目录 一、测试环境 1、系统环境 2、使用工具/软件 二、测试目的 三、操作过程 1、抓包使用burp生成csrf脚本 四、源代码分析 五、结论 一、测试环境 1、系统环境 渗透机&#xff1a;本机(127.0.0.1) 靶 机&#xff1a;本机(127.0.0.1) 2、使用工具/软件 Burp sui…

老机MicroServer Gen8再玩 OCP万兆光口+IT直通

手上有一台放了很久的GEN8微型服务器&#xff0c;放了很多年&#xff0c;具体什么时候买的我居然已经记不清了 只记得开始装修的时候搬家出去就没用了&#xff0c;结果搬出去有了第1个孩子&#xff0c;孩子小的时候也没时间折腾&#xff0c;等孩子大一点的时候&#xff0c;又有…

【去哪儿-注册安全分析报告-缺少轨迹的滑动条】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

RabbitMQ service is already present - only updating service parameters

Windows下卸载RabbitMQ之后,然后重新注册RabbitMQ服务的时候,报错以下信息: D:\software\rabbitmq-server-4.0.2\rabbitmq_server-4.0.2\sbin>D:\software\rabbitmq-server-4.0.2\rabbitmq_server-4.0.2\sbin\rabbitmq-service.bat install RabbitMQ service is already …

kafka自定义配置信息踩坑

org.apache.kafka.common.config.ConfigException: Invalid value 0 for configuration acks: Expected value to be a string, but it was a java.lang.Integer 场景描述&#xff1a; 单个kafka使用springboot框架自带的 yml 配置完全OK&#xff08;因为底层会帮我们处理好类…

调查显示软件供应链攻击增加

OpenText 发布了《2024 年全球勒索软件调查》&#xff0c;强调了网络攻击的重要趋势&#xff0c;特别是在软件供应链中&#xff0c;以及生成式人工智能在网络钓鱼诈骗中的使用日益增多。 尽管各国政府努力加强网络安全措施&#xff0c;但调查显示&#xff0c;仍有相当一部分企…

CentOS7安装RabbitMQ-3.13.7、修改端口号

本文安装版本&#xff1a; Erlang&#xff1a;26.0 官网下载地址 Erlang RabbitMQ&#xff1a;3.13.7 官网下载地址 RabbitMQ RabbitMQ和Erlang对应关系查看&#xff1a;https://www.rabbitmq.com/which-erlang.html 注&#xff1a;安装erlang之前先安装下依赖文件&#xff0…

【Qt】控件——Qt多元素控件、常见的多元素控件、多元素控件的使用、List Widget、Table Widget、Tree Widget

文章目录 QtQt多元素控件List WidgetTable WidgetTree Widget Qt Qt多元素控件 List Widget 使用 QListWidget 能够显示一个纵向的列表。 属性说明currentRow当前被选中的是第几行。count一共有多少行。sortingEnabled是否允许排序。isWrapping是否允许换行。itemAlignment元素…

Tomcat日志文件详解及catalina.out日志清理方法

目录 前言1. Tomcat日志文件详解1.1 catalina.out1.2 localhost_access_log1.3 catalina.<date>.log1.4 host-manager.<date>.log 和 manager.<date>.log1.5 localhost.<date>.log 2. catalina.out文件管理与清理方法2.1 为什么不能直接删除catalina.o…

【火山引擎】AIGC图像风格化 | 风格实践 | PYTHON

目录 1 准备工作 2 实践 代码 效果图 1 准备工作 ① 服务开通 确保已开通需要访问的服务。您可前往火山引擎控制台,在左侧菜单中选择或在顶部搜索栏中搜索需要使用的服务,进入服务控制台内完成开通流程。

开发工具(上)

前面我们在Linux部分了解文件权限&#xff0c;和基本指令的内容&#xff0c;但对于开发工具还是没有很多的接触&#xff0c;现在这一篇就是主要讲基础的工具&#xff1b;如yum&#xff0c;yum源&#xff0c;包管理器等等&#xff1b; Linux中的安装软件&#xff1a; 源码安装 …

JAVA——IO流(2)

目录 1.缓冲流 a.字节缓冲流 1.构造方法 2.常见方法 3.底层原理 b.字符缓冲流 1.构造方法 2.特有方法 2.转换流 a.概述 b.编码问题 c.InputStreamReader类 1.概述 2.构造方法 d.OutputStreamWrite类 1.概述 2.构造方法 3.序列化流 a.概述 b.ObjectOutputStr…

TCP的建立与终止——三次握手、四次挥手

目录 1. UDP和TCP的区别 2. TCP概述 3. TCP连接的建立&#xff08;三次握手&#xff09; 3.1 为什么TCP客户端最后还要发送一次确认&#xff1f; 3.2 什么是半连接队列&#xff1f; 3.3 半连接队列被填满或遇到SYN洪泛攻击是如何处理&#xff1f; 3.4 三次握手过程中可以…

力扣 简单 746.使用最小花费爬楼梯

文章目录 题目介绍题解 题目介绍 题解 思路分析&#xff1a; 确定dp数组以及下标的含义&#xff1a;dp[i]的定义为到达第i台阶所花费的最少体力。确定递推公式&#xff1a;可以有两个途径得到dp[i]&#xff0c;一个是dp[i-1] 一个是dp[i-2]。dp[i - 1] 跳到 dp[i] 需要花费 d…

Linux修改npm的镜像源为淘宝镜像

起因&#xff1a;使用官方镜像源下载软件包速度太慢 1.查看npm当前镜像源命令 npm get registry 执行结果 2.还原为官方镜像源命令 npm config set registry https://registry.npmjs.org/ 3.修改为淘宝镜像命令 npm config set registry https://registry.npmmirror.com …

神经网络激活函数定义速查

一、概述 老猿记心不太好&#xff0c;虽然最近写了两篇博文介绍激活函数&#xff1a;神经网络激活函数列表大全及keras中的激活函数定义&#xff1a;https://blog.csdn.net/LaoYuanPython/article/details/142731106》、《神经网络高级激活函数大全及keras中的函数定义 https:…

安装jianmu

一说明 1建木可以代替jkenis进行自动化部署**二安装 2.1登录官网&#xff0c;查看命令** 官网地址&#xff1a;https://docs.jianmu.dev/guide/how-to-deploy.html#%E9%83%A8%E7%BD%B2%E6%96%B9%E5%BC%8F%E4%B8%80%EF%BC%9Adocker-compose%E9%83%A8%E7%BD%B2 2.2拉取yml文件…