C++之模版进阶篇

目录

前言

1.非类型模版参数

2.模版的特化

2.1概念

2.2函数模版特化

2.3 类模板特化

2.3.1 全特化和偏特化

2.3.2类模版特化应用实例

3.模版分离编译

3.1 什么是分离编译

3.2 模板的分离编译

3.3 解决方法

4. 模板总结

结束语


前言

在模版初阶我们学习了函数模版和类模版的相关知识,对模版有了一定的了解,接下来我们将对模版进行进一步的了解,话不多说,直接上货!!!

本节内容

1. 非类型模板参数
2. 类模板的特化
3. 模板的分离编译

1.非类型模版参数

模板参数分类类型形参与非类型形参
类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称
非类型形参,就是 用一个常量作为类(函数)模板的一个参数 ,在类 ( 函数 ) 模板中可将该参数当成常量来使用

比如我们简单写一个静态栈,可以定义不同大小的栈,这里我们没有传默认参数,传也可以。

template<size_t N>
class Stack {
private:int _a[N];int _top;
};
int main() {Stack<5>s1;Stack<10>s2;return 0;
}
其实非类型形参在大小定义方面用的比较多,把大小当做常量。对于传参都传的是整数。
注意:
1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的
2. 非类型的模板参数必须在编译期就能确认结果
在C++11版本的array用了 非类型的模板参数, 来代替静态数组。
#include <array>
#include <iostream>
using namespace std;
int main(){array<int, 10> a1;array<int, 100> a2;
int a3[10];
return 0;
}

那么用array定义数组的好处是什么呢,是数组初始化吗,当然不是。

相比较于int a3[10],其实array的好处是越界检查方面,array对于数组的读和写都会进行越界检查,而int a3[10],对于读不会检查,而写会检查,进行的是抽取,一般会在数组后面默认分配两个数据大小空间左右。

 array<int, 10> a1;array<int, 100> a2;
// a1[100] = 3;int a3[10];cout << a3[100] << endl;a3[10] = 1;
// a3[12] = 1;

array底层访问是调用了一个函数进行实现,都会调用assert语句来判断。

template<class T, size_t N = 10>
class array
{
public:T& operator[](size_t index) { assert(index < N);return _array[index];}
private:T _array[N];size_t _size;
};

其实对于数组的定义,在C++中我们采用vector来定义,可以进行初始化

vector<int>v(10,1);

但是如果要频繁的开设数组,其实array的效率可能要高些,因为array是在栈上开空间的,栈是向下生长的,vector是在堆上。 

2.模版的特化

2.1概念

通常情况下, 使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结 ,需要特殊处理,比如:实现一个专门用来小于比较的函数模版
class Date {
public:Date(int year = 1, int month = 1, int day = 1):_year(year),_month(month),_day(day){}bool operator<(const Date& other) const {if (_year != other._year) return _year < other._year;if (_month != other._month) return _month < other._month;return _day < other._day;}/*friend ostream&operator<<(ostream& out, const Date& d) {out << d._year << "-" << d._month << "-" << d._day;return out;}*/
private:int _year;int _month;int _day;
};
template <class T>
bool Less(const T &left,const T& right) {return left < right;
}
int main() {cout << Less(1, 2) << endl; // 可以比较,结果正确Date d1(2024, 7, 9);Date d2(2024, 7, 8);cout << Less(d1, d2) << endl; // 可以比较,结果正确Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; // 可以比较,结果错误return 0;
}

 

可以看到, Less 绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果。上述示例中, p1 指向的d1 显然大于 p2 指向的 d2 对象,但是 Less内部并没有比较p1p2指向的对象内容,而比较的是p1p2 指针的地址,这就无法达到预期而错误。
此时,就 需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式 。模板特化中分为函数模板特化 类模板特化

2.2函数模版特化

函数模板的特化步骤:
1. 必须要先有一个基础的函数模板
2. 关键字template后面接一对空的尖括号<>
3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
template <class T>
bool Less(const T &left,const T& right) {return left < right;
}template<>
bool Less<Date*>(Date* const& left, Date* const& right)
{return *left < *right;
}
/*
template<>
bool Less<const Date*>(const Date* const& left, const Date* const& right)
{return *left < *right;
}
*/

注意:const要放在*之后,修饰的才是引用变量本身,放在*之前是修饰指向内容

非const特化

template <class T>
bool Less( T &left,T& right) {return left < right;
}template<>
bool Less<Date*>(Date* &left, Date* &right)
{return *left < *right;
}

所以对于下面这段代码

Date * p1 = & d1 ;
Date * p2 = & d2 ;
cout << Less ( p1 , p2 ) << endl ; // 调用特化之后的版本,而不走模板生成了
注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给 出。
bool Less(Date* left, Date* right)
{return *left < *right;
}
该种实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化时特别给出,因此函数模板不建议特化。

2.3 类模板特化

2.3.1 全特化和偏特化

全特化即是将模板参数列表中所有的参数都确定化。
偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。
template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};
//全特化
template<>
class Data<int, char>
{
public:Data() { cout << "Data<int, char>" << endl; }
private:int _d1;char _d2;
};
//偏特化
template<class T1>
class Data<T1, int> {
public:Data() { cout << "Data<T1, int>" << endl; }
private:T1 _d1;int _d2;
};int main() {Data<int, int>d1;Data<int, char>d2;Data<int, int> d3;return 0;
}

偏特化有以下两种表现方式:

部分特化

将模板参数类表中的一部分参数特化。
 
// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:Data() {cout<<"Data<T1, int>" <<endl;}
private:T1 _d1;int _d2;
};
参数更进一步的限制
偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本
/两个参数偏特化指针类型
//template<class T1,class T2>
template<typename T1, typename T2>
class Data<T1*, T2*>
{
public:Data()  { cout << "Data<T1*, T2*>" << endl;  }
private:T1 _d1;T2 _d2;
};
//两个参数特化引用类型template<class T1, class T2>class Data<T1&, T2&>{public:Data()  {  cout << "Data<T1&, T2&>" << endl; }private:T1 _d1;T2 _d2;};//一个指针一个引用template<class T1, class T2>class Data<T1&, T2*>{public:Data() { cout << "Data<T1&, T2*>" << endl; }private:T1 _d1;T2 _d2;};

2.3.2类模版特化应用实例

实例1:

class Date {
public:Date(int year = 1, int month = 1, int day = 1):_year(year),_month(month),_day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}friend ostream&operator<<(ostream& out, const Date& d) {out << d._year << "-" << d._month << "-" << d._day;return out;}
private:int _year;int _month;int _day;
};
/*
template <class T>
bool Less(const T &left,const T& right) {return left < right;
}
*/
template<class T>
struct Less
{bool operator()(const T& x, const T& y) const{return x < y;}
};
int main() {vector<Date>d1;d1.push_back(Date(2024, 8, 25));d1.push_back(Date(2024, 8, 24));d1.push_back(Date(2024, 8, 27));sort(d1.begin(), d1.end(), Less<Date>());for (const auto& date : d1) {cout << date << endl;}return 0;
}

 实例2:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Date {
public:Date(int year = 1, int month = 1, int day = 1):_year(year),_month(month),_day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}friend ostream&operator<<(ostream& out, const Date& d) {out << d._year << "-" << d._month << "-" << d._day;return out;}private:int _year;int _month;int _day;
};
template<class T>
class Less
{
public:bool operator()(const T& x, const T& y) const{return x < y;}
};
// 对Less类模板按照指针方式特化/*
template <class T>
bool Less(const T &left,const T& right) {return left < right;
}
*/int main()
{Date d1(2022, 7, 7);Date d2(2022, 7, 6);Date d3(2022, 7, 8);vector<Date> v1;v1.push_back(d1);v1.push_back(d2);v1.push_back(d3);// 可以直接排序,结果是日期升序sort(v1.begin(), v1.end(), Less<Date>());for (const auto& date : v1) {cout << date << endl;}vector<Date*> v2;v2.push_back(&d1);v2.push_back(&d2);v2.push_back(&d3);// 可以直接排序,结果错误日期还不是升序,而v2中放的地址是升序// 此处需要在排序过程中,让sort比较v2中存放地址指向的日期对象// 但是走Less模板,sort在排序时实际比较的是v2中指针的地址,因此无法达到预期sort(v2.begin(), v2.end(), Less<Date*>());for (const auto& date : v2) {cout << *date << endl;}return 0;
}

通过观察上述程序的结果发现,对于日期对象可以直接排序,并且结果是正确的。但是如果待排序元素是指针,结果就不一定正确。因为:sort 最终按照 Less 模板中方式比较,所以只会比较指针,而不是比较指针指向空间中内容,此时可以使用类版本特化来处理上述问题:
// 对Less类模板按照指针方式特化
template<>
struct Less<Date*>
{bool operator()(Date* x, Date* y) const{return *x < *y;}
};

 

3.模版分离编译

3.1 什么是分离编译

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

3.2 模板的分离编译

假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义。
// a.h
template<class T>
T Add(const T& left, const T& right);
// a.cpp
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}
// main.cpp
#include"a.h"
int main()
{Add(1, 2);Add(1.0, 2.0);return 0;
}

C/C++程序的运行过程通常包括以下几个步骤:
1. 预处理(Preprocessing):
   - 预处理器处理源代码文件中的预处理指令,如 `#include`、`#define`、`#if`、`#ifdef` 等。
   - 预处理器将包含的文件内容插入到源文件中,处理宏定义,条件编译指令等。
   - 结果是一个扩展的源代码文件,通常以 `.i` 文件(C语言)或 `.ii` 文件(C++语言)表示。
2. 编译(Compilation):
   - 编译器将预处理后的源代码翻译成汇编语言。
   - 在这个阶段,编译器进行词法分析、语法分析、语义分析、中间代码生成和优化等操作。
   - 生成的汇编语言文件通常以 `.s` 文件表示。
3. 汇编(Assembly):
   - 汇编器将汇编语言转换成机器语言指令,这些指令是二进制形式的,可以被计算机的CPU直接执行。
   - 生成的目标代码文件通常以 `.o`(Linux/Unix系统)或 `.obj`(Windows系统)表示。
4. 链接(Linking):
   - 链接器将一个或多个目标文件以及所需的库文件合并成一个可执行文件。
   - 在这个阶段,链接器解析外部引用和符号,合并相同的函数和数据,进行重定位等操作。
   - 最终生成的可执行文件通常以 `.exe`(Windows系统)或无扩展名(Linux/Unix系统)表示。
5. 加载(Loading):
   - 当程序运行时,操作系统负责将可执行文件加载到内存中。
   - 加载器读取可执行文件,并将程序代码和数据映射到内存的适当位置。
6. 执行(Execution):
   - CPU开始执行程序的主函数,程序正式开始运行。
   - 在执行过程中,程序可能会进行各种计算、输入输出操作、调用库函数等。
7. 终止(Termination):
   - 程序执行完成后,或者遇到无法处理的错误时,程序会终止。
   - 在终止前,程序可能需要执行一些清理工作,如关闭打开的文件、释放分配的内存等。
 

链接之前各文件没有交互,模版没有实例化没有生成指令,代码,链接就出现问题。

3.3 解决方法

1. 将声明和定义放到一个文件 "xxx.hpp" 里面或者 xxx.h 其实也是可以的 推荐
用的地方就直接有定义了。
2. 模板定义的位置显式实例化 。这种方法不实用,不推荐。

显示实例化:

template

int Add(const int&left,const int&right)

对于其他类型照样,所以比较复杂

4. 模板总结

【优点】
1. 模板复用了代码,节省资源,更快的迭代开发, C++ 的标准模板库 (STL) 因此而产生
2. 增强了代码的灵活性
【缺陷】
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

 
 

结束语

本篇就到此结束啦,内容有点多,相信大家通过本篇博客,对模版的认识有了进一步的理解。

最后感谢各位友友的支持,支持小编的留个赞吧!!!

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

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

相关文章

【MySQL】Ubuntu环境下MySQL的安装与卸载

目录 1.MYSQL的安装 2.MySQL的登录 3.MYSQL的卸载 4.设置配置文件 1.MYSQL的安装 首先我们要看看我们环境里面有没有已经安装好的MySQL 我们发现是默认是没有的。 我们还可以通过下面这个命令来确认有没有mysql的安装包 首先我们得知道我们当前的系统版本是什么 lsb_…

你还在为教学资料转换烦恼吗?4款神器安利给你,PDF转JPG一键搞定

工作或者学习的时候&#xff0c;我们经常得把PDF文件转换成JPG图片。可能是因为在手机上看起来方便&#xff0c;或者是想放到PPT里展示&#xff0c;反正把PDF转JPG的情况挺多的。那有什么好用的软件能做这个转换呢&#xff1f;今天我就给你们介绍几个好用的。 1. 福昕PDF高质量…

儿童需要学习C++多久才能参加信息学奥赛的CSP-J比赛?

信息学奥赛&#xff08;NOI&#xff09;是国内编程竞赛领域的顶尖赛事&#xff0c;而对于初学者来说&#xff0c;参加NOI的第一步通常是通过CSP-J&#xff08;全国青少年信息学奥林匹克联赛初赛&#xff09;&#xff0c;这也是面向青少年程序员的入门级竞赛。作为信息学奥赛的基…

【解决办法】git clone报错unable to access ‘xxx‘: SSL certificate problem:

使用git clone 时报错unable to access xxx: SSL certificate problem: 这个报错通常是由于SSL证书问题引起的。通常可以按照以下步骤进行排查&#xff1a; 检查网络连接&#xff1a;确保你的网络连接正常&#xff0c;可以访问互联网。尝试使用其他网站或工具测试网络连接是否正…

基于SpringBoot vue3 的山西文旅网java网页设计与实现

博主介绍&#xff1a;专注于Java&#xff08;springboot ssm springcloud等开发框架&#xff09; vue .net php phython node.js uniapp小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作☆☆☆ 精彩专栏推荐订阅☆☆☆☆…

LeetCode讲解篇之1043. 分隔数组以得到最大和

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 对于这题我们这么考虑&#xff0c;我们选择以数字的第i个元素做为分隔子数组的右边界&#xff0c;我们需要计算当前分隔子数组的长度为多少时能让数组[0, i]进行分隔数组的和最大 我们用数组f表示[0, i)区间内的…

docker 部署 Seatunnel 和 Seatunnel Web

docker 部署 Seatunnel 和 Seatunnel Web 说明&#xff1a; 部署方式前置条件&#xff0c;已经在宿主机上运行成功运行文件采用挂载宿主机目录的方式部署SeaTunnel Engine 采用的是混合模式集群 编写Dockerfile并打包镜像 Seatunnel FROM openjdk:8 WORKDIR /opt/seatunne…

自动驾驶-问题笔记-待解决

参考线的平滑方法 参考线平滑算法主要有三种&#xff1a; 离散点平滑&#xff1b;螺旋曲线平滑&#xff1b;多项式平滑&#xff1b; 参考链接&#xff1a;参考线平滑 对于平滑方法&#xff0c;一直不太理解平滑、拟合以及滤波三者的作用与区别&#xff1b; 规划的起点&#x…

leetcode第189题:轮转数组(C语言版)

思路1&#xff08;不推荐&#xff09; 保存数组最后一个元素&#xff0c;然后数组全体元素后移一位&#xff0c;把保存的最后一个元素存放到数组的第一个位置&#xff0c;重复这一操作&#xff0c;直到执行完了k次。 时间复杂度&#xff1a;需要用k次循环&#xff0c;里面套一层…

MySQL的驱动安装

1、下载并安装MySQL 下载地址&#xff1a; 建议在下列框中选择LTS长期支持版本&#xff0c;下载对应的MSI安装文件。 安装完成后&#xff0c;将MySQL的环境bin路径添加到环境变量中。 可以运行MySQL Configurator进行配置&#xff0c;主要设置密码&#xff0c;并初始化。其余…

【五分钟学会】YOLO11 自定义数据集从训练到部署

数据集地址 数据集包含 360 张红血细胞图像及其注释文件&#xff0c;分为训练集与验证集。训练文件夹包含 300 张带有注释的图像。测试和验证文件夹都包含 60 张带有注释的图像。我们对原始数据集进行了一些修改以准备此 CBC 数据集&#xff0c;并将数据集分成三部分。在360张…

<<迷雾>> 第8章 学生时代的走马灯(3)--走马灯 示例电路

几个首尾相连的触发器使用同一个控制端&#xff0c;能同时触发 info::操作说明 鼠标单击开关切换开合状态 注: 其中 CP 为按钮开关, 每点击一次, Q 的输出前进一级 注: 第一个触发器的输出端 Q 需要先置入高电平. 如果重置了电路, 可外接电源先使第一个 Q 置入高电平. 另: 因为…

【Windows】在任务管理器中隐藏进程

在此前的一篇&#xff0c;我们已经介绍过了注入Dll 阻止任务管理器结束进程 -- Win 10/11。本篇利用 hook NtQuerySystemInformation 并进行断链的方法实现进程隐身&#xff0c;实测支持 taskmgr.exe 的任意多进程隐身。 任务管理器 代码&#xff1a; // dllmain.cpp : 定义 …

【中间件学习】Git的命令和企业级开发

一、Git命令 1.1 创建Git本地仓库 仓库是进行版本控制的一个文件目录。我们要想对文件进行版本控制&#xff0c;就必须创建出一个仓库出来。创建一个Git本地仓库对应的命令是 git init &#xff0c;注意命令要在文件目录下执行。 hrxlavm-1lzqn7w2w6:~/gitcode$ pwd /home/hr…

No.10 笔记 | PHP学习指南:PHP数组掌握

本指南为PHP开发者提供了一个全面而简洁的数组学习路径。从数组的基本概念到高级操作技巧&#xff0c;我们深入浅出地解析了PHP数组的方方面面。无论您是初学者还是寻求提升的中级开发者&#xff0c;这份指南都能帮助您更好地理解和运用PHP数组&#xff0c;提高编码效率和代码质…

python之运算符

1、算术运算符 算术运算符常用的有&#xff1a;&#xff0c;-&#xff0c;*&#xff0c; &#xff0c;/&#xff0c;//&#xff0c;%&#xff0c;>>,<< 1.1、加 常见的是算术相加&#xff0c;还有一种是字符串拼接。 a 10 b 20 print(a b) c "My &quo…

基于facefusion的换脸

FaceFusion是一个引人注目的开源项目&#xff0c;它专注于利用深度学习技术实现视频或图片中的面部替换。作为下一代换脸器和增强器&#xff0c;FaceFusion在人脸识别和合成技术方面取得了革命性的突破&#xff0c;为用户提供了前所未有的视觉体验。 安装 安装基础软件 安装…

注册安全分析报告:科研诚信查询平台无验证方式导致安全隐患

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

15分钟学 Python 第40天:Python 爬虫入门(六)第一篇

Day40 &#xff1a;Python 爬取豆瓣网前一百的电影信息 1. 项目背景 在这个项目中&#xff0c;我们将学习如何利用 Python 爬虫技术从豆瓣网抓取前一百部电影的信息。通过这一练习&#xff0c;您将掌握网页抓取的基本流程&#xff0c;包括发送请求、解析HTML、存储数据等核心…

m4a怎么转换成mp3?音频转换MP3只需要这6个小工具!

m4a怎么转换成mp3&#xff1f;M4A和MP3是两种常见的音频格式&#xff0c;M4A通常使用AAC&#xff08;高级音频编码&#xff09;进行压缩&#xff0c;提供更高的音质和更小的文件体积&#xff0c;特别适合在Apple设备上使用。而MP3则以其高压缩比和广泛的兼容性著称&#xff0c;…