【C++】模板(进阶)

本篇我们来介绍更多关于C++模板的知识。模板初阶移步至:【C++】模板(初阶) 

1.非类型模板参数

1.1 非类型模板参数介绍

模板参数可以是类型形参,也可以是非类型形参。类型形参就是我们目前接触到的一些模板参数。

//类型模板参数
template<class T, class Container = vector<T>, class Compare = Less<T>>

非类型模板参数就是用一个常量作为模板的参数,在模板中可以将该参数当作常量使用。

//N为非类型模板参数
template<class T, size_t N = 10> //N给缺省值
template<size_t N> //N没给缺省值

比如说我们要弄一个数组长度是固定的栈。

template<size_t N>
class Stack
{
private:int _a[N]; //定长数组//...
};

在使用的时候就可以传想要的N的值。

int main()
{Stack<5> st1; //存5个数据Stack<10> st2; //存10个数据return 0;
}

1.1.1 和C语言宏对比

这个和C语言的宏区别还是很大的,C语言的宏只能存5个或者10个数据,不可以像上面这样st1存5个值,st2又能存10个数据。

#define N 5  //宏,此时st1和st2都是存5个数据
class Stack
{
private:int _a[N]; //定长数组//...
};int main()
{Stack st1; Stack st2; return 0;
}

1.1.2 底层原理及注意事项

非类型模板参数和传类型模板参数,模板的底层原理都是一样的,都是生成了不同的类,如果是函数模板就是生成了不同的函数

如果非类型模板参数给了缺省值,可以不传参,但是要加上<>。

template<size_t N = 5> //给缺省值
class Stack
{
private:int _a[N]; //定长数组//...
};int main()
{Stack<> st1; //不传参Stack<10> st2; //传参return 0;
}

不传参也不加<>的写法在C++20才支持,这里还是建议加上<>。 

注意:

1.非类型模板参数只能用于整形(bool也算整形),浮点数(C++20才支持)、类对象以及字符串是不允许作为非类型模板参数的。

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

 我们也可以有多个非类型模板参数。

template<size_t N = 5, bool fg = true> //给缺省值

1.2 array介绍

array是一个容器,底层就是一个静态的数组,它就用到了非类型模板参数

相关文档:array - C++ Reference  ,使用时包含头文件 #include <array>

第一个模板参数T类型模板参数,第二个模板参数N非类型模板参数。 

array支持迭代器也支持下标访问,array就没有头删尾删、头插尾插这样的接口了,因为它是定长的。

我们来用一下array,假如要定义一个类型为int,长度为10的数组。

array<int, 10> a1;

等同于 int a1[10]; 

array对于数组越界的检查是比较严格的,比如下面的例子。

int a2[10];
cout << a2[10] << endl;//读越界的位置

未报错。

但是 array的越界检查是比较严格的,会直接报错

补充一句:array的数据是存在栈上的,vector的数据是存在堆上的。 

2.模板的特化

我们在使用模板的时候,有些情况下对于一些特殊类型的结果可能不是我们想要的。这时我们就可以使用模板的特化。

2.1 函数模板的特化

比如说我们用函数模板实现两个数的小于比较。

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

在通常情况下这个模板是没有问题的,像传一些int、double这样的内置类型。但是如果传自定义类型可能结果就不是我们想要的,比如说Date类,我们就可以用模板的特化。

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

当我们传参类型为Date*时,就会直接走特化的模板,传别的类型的参数时,还是走函数模板那一套。

2.2 类模板的特化

2.2.1 全特化

全特化就是将模板参数列表中所有的参数都确定化。假如这里有一个如下的类。

template<class T1, class T2>
class Date
{
public:Date(){cout << "Date<T1, T2>" << endl;}
};

对这个类全特化就是在类名后面加上<>,<>里面写上具体类型。

template<>
class Date<int, char>
{
public:Date(){cout << "Date<int, char>" << endl;}
};

如果我们传int和char类型的参数过去,就直接匹配调用特化了的类,其余情况都调用正常的类模板。

int main()
{Date<int, int> d1;Date<int, char> d2;return 0;
}

2.2.2 偏特化(半特化)

偏特化有两种表现方式,部分特化参数更进一步的限制

部分特化

这种特化就是只特化一部分,如下。

template<class T1>
class Date<T1, char>
{
public:Date(){cout << "Date<int, char>" << endl;}
};

只要是第二个参数传的是char类型,就会匹配到这个偏特化(半特化)的类,其余情况还是匹配正常的类模板。

int main()
{Date<int, int> d1;Date<char, char> d2;Date<double, char> d3;return 0;
}

当全特化和偏特化(半特化)同时存在时,会优先选择全特化

template<class T1, class T2> //正常类模板
class Date 
{
public:Date(){cout << "Date<T1, T2>" << endl;}
};template<>  //全特化
class Date<int, char>
{
public:Date(){cout << "Date<int, char>" << endl;}
};template<class T1>  //偏特化
class Date<T1, char> 
{
public:Date(){cout << "Date<T1, char>" << endl;}
};int main()
{Date<int, char> d2; //优先选择全特化return 0;
}

 参数更进一步的限制

这种特化针对模板参数更进一步的条件限制所设计出来的一个特化版本。比如说下面这个。

template<class T1, class T2>
class Date<T1*, T2*>   //指针
{
public:Date(){cout << "Date<T1*, T2*>" << endl;}
};

这个特化的意思就是,只要参数传的是指针不管什么类型的指针,都走这个模板。比如说下面的例子。

int main()
{Date<int, char> d1;Date<int*, char*> d2; //传的指针Date<double*, char*> d3; //传的指针Date<int*, double*> d4; //传的指针Date<char*, int*> d5; //传的指针return 0;
}

除了特化成指针,还能特化成引用

template<class T1, class T2>
class Date<T1&, T2&>    //引用
{
public:Date(){cout << "Date<T1&, T2&>" << endl;}
};
Date<int&, char&> d6; //传的引用
Date<double&, char&> d7; //传的引用
Date<int&, double&> d8; //传的引用
Date<char&, int&> d9; //传的引用

两者混合也可以,一个指针一个引用。

template<class T1, class T2>
class Date<T1&, T2*>
{
public:Date(){cout << "Date<T1&, T2*>" << endl;}
};

易错点

我们先看下面这段代码。

template<class T1, class T2>
class Date<T1*, T2*>
{
public:Date(){cout << "Date<T1*, T2*>" << endl;T1 t;cout << typeid(t).name() << endl;}
};
int main()
{Date<int*, int*> d1;
}

代码中的t是什么类型?是 int* 还是 int?

是int。

如果传过去的是int**,t的类型就是int*。

Date<int**, int*> d1;

在特化的时候,除了指针这里会比较容易混淆,引用也是一样,比如下面这个例子。

template<class T1, class T2>
class Date<T1&, T2&>
{
public:Date(){cout << "Date<T1&, T2&>" << endl;T1 t;cout << typeid(t).name() << endl;}
};
void test6()
{Date<int&, int&> d1;
}

此时t的类型就是int,而不是int类型的引用。

所以,我们想定义一个指针或者引用的时候应该要像下面这样。

template<class T1, class T2>
class Date<T1&, T2*>
{
public:Date(){cout << "Date<T1&, T2*>" << endl;int a = 0;T1& t1 = a;  //定义引用T2* t2 = &a; //定义指针//...}
};

因为T1和T2在上面的情况下都是int类型,而不是我们想要的引用和指针。

3.模板分离编译及优缺点

3.1 模板的分离编译

我之前的很多篇博客用到了声明和定义分离的方法,主要是一些像模拟实现这样的代码较多的程序,随着我们更深入的学习,一个程序的代码量是会越来越多的,声明和定义分离可以让我们的代码更有条理。

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

程序要运行,一般要经历这几个步骤:预处理 -> 编译 -> 汇编 -> 链接

 而模板,是不支持声明和定义分离的,会发生链接错误。

所以建议模板的声明和定义放在同一个文件中,不要分离。 

3.2 模板的优缺点

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

本次分享就到这里,我们下篇再见~

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

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

相关文章

一文学会YOLO系列算法(从V3到11)实现遥感图像目标检测

目录 前言 数据集介绍 数据集转换 YOLO代码的下载 YOLO的配置 1.数据集的配置 2.模型的配置 YOLO11模型的训练 其它版本YOLO的训练 前言 遥感技术的快速发展&#xff0c;特别是在高分辨率遥感图像的获取能力上的显著提升&#xff0c;已经大大拓宽了遥感数据在环境监测…

图解Git——分布式Git《Pro Git》

分布式工作流程 Centralized Workflow&#xff08;集中式工作流&#xff09; 所有开发者都与同一个中央仓库同步代码&#xff0c;每个人通过拉取、提交来合作。如果两个开发者同时修改了相同的文件&#xff0c;后一个开发者必须在推送之前合并其他人的更改。 Integration-Mana…

【高阶数据结构】布隆过滤器(BloomFilter)

1. 概念 1.1 背景引入 背景&#xff1a;在计算机软件中&#xff0c;一个常见的需求就是 在一个集合中查找一个元素是否存在 &#xff0c;比如&#xff1a;1. Word 等打字软件需要判断用户键入的单词是否在字典中存在 2. 浏览器等网络爬虫程序需要保存一个列表来记录已经遍历过…

【json_object】mysql中json_object函数过长,显示不全

问题&#xff1a;json只显示部分 解决&#xff1a; SET GLOBAL group_concat_max_len 1000000; -- 设置为1MB&#xff0c;根据需要调整如果当前在navicat上修改&#xff0c;只有效本次连接和后续会话&#xff0c;重新连接还是会恢复默认值1024 在my.ini配置文件中新增或者修…

计算机网络 (52)秘钥分配

一、重要性 在计算机网络中&#xff0c;密钥分配是密钥管理中的一个核心问题。由于密码算法通常是公开的&#xff0c;因此网络的安全性主要依赖于密钥的安全保护。密钥分配的目的是确保密钥在传输过程中不被窃取或篡改&#xff0c;同时确保只有合法的用户才能获得密钥。 二、方…

第35天:安全开发-JavaEE应用原生反序列化重写方法链条分析触发类类加载

时间轴&#xff1a; 序列化与反序列化图解&#xff1a; 演示案例&#xff1a; Java-原生使用-序列化&反序列化 Java-安全问题-重写方法&触发方法 Java-安全问题-可控其他类重写方法 Java-原生使用-序列化&反序列化 1.为什么进行序列化和反序列化&#xff1…

MindAgent:基于大型语言模型的多智能体协作基础设施

2023-09-18 &#xff0c;加州大学洛杉矶分校&#xff08;UCLA&#xff09;、微软研究院、斯坦福大学等机构共同创建的新型基础设施&#xff0c;目的在评估大型语言模型在游戏互动中的规划和协调能力。MindAgent通过CuisineWorld这一新的游戏场景和相关基准&#xff0c;调度多智…

Excel 技巧17 - 如何计算倒计时,并添加该倒计时的数据条(★)

本文讲如何计算倒计时&#xff0c;并添加该倒计时的数据条。 1&#xff0c;如何计算倒计时 这里也要用公式 D3 - TODAY() 显示为下面这个样子的 然后右键该单元格&#xff0c;选 设置单元格格式 然后点 常规 这样就能显示出还书倒计时的日数了。 下拉适用到其他单元格。 2&a…

rocketmq基本架构

简介 Name server 负责broker注册、心跳&#xff0c;路由等功能&#xff0c;类似Kafka的ZKname server节点之间不互相通信&#xff0c;broker需要和所有name server进行通信。扩容name server需要重启broker&#xff0c;不然broker不会和name server建立连接producer和consum…

国产编辑器EverEdit - 大纲视图

1 大纲视图 1.1 应用场景 在编辑较长代码文件时&#xff0c;使用大纲视图可以方便的检视当前文件的变量、函数等信息&#xff0c;方便在不同函数间跳转&#xff0c;对整个文档的全貌了然于胸。   在编辑XML文档时&#xff0c;通过展示XML文件的层次结构、节点布局&#xff0…

Linux中的基本指令(一)

一、Linux中指令的存在意义 Linux中&#xff0c;通过输入指令来让操作系统执行&#xff0c;以此达到控制操作系统的目的&#xff0c;类似于Windows中的双击&#xff0c;右键新建文件&#xff0c;新建文件夹等 1.补&#xff1a;关于屏幕的几个操作指令 ①清屏指令 clear 回…

2025/1/21 学习Vue的第四天

睡觉。 --------------------------------------------------------------------------------------------------------------------------------- 11.Object.defineProperty 1.在我们之前学习JS的时候&#xff0c;普通得定义一个对象与属性。 <!DOCTYPE html> <h…

Go Map 源码分析(一)

Go语言中的map是通过哈希表实现的&#xff0c;其底层结构和实现机制如下&#xff1a; 一、hash 结构 hmap结构体&#xff1a;是map的头部结构&#xff0c;主要字段及含义如下&#xff1a; count&#xff1a;表示当前哈希表中的元素数量&#xff0c;与len()函数相对应。flags…

Linux-C/C++--深入探究文件 I/O (上)(文件的管理、函数返回错误、exit()、_Exit()、_exit())

经过上一章内容的学习&#xff0c;相信各位读者对 Linux 系统应用编程中的基础文件 I/O 操作有了一定的认识和理解了&#xff0c;能够独立完成一些简单地文件 I/O 编程问题&#xff0c;如果你的工作中仅仅只是涉及到一些简单文件读写操作相关的问题&#xff0c;其实上一章的知识…

【机器学习实战中阶】音乐流派分类-自动化分类不同音乐风格

音乐流派分类 – 自动化分类不同音乐风格 在本教程中,我们将开发一个深度学习项目,用于自动化地从音频文件中分类不同的音乐流派。我们将使用音频文件的频率域和时间域低级特征来分类这些音频文件。 对于这个项目,我们需要一个具有相似大小和相似频率范围的音频曲目数据集…

Walrus Learn to Earn计划正式启动!探索去中心化存储的无限可能

本期 Learn to Earn 活动将带领开发者和区块链爱好者深入探索 Walrus 的技术核心与实际应用&#xff0c;解锁分布式存储的无限可能。参与者不仅能提升技能&#xff0c;还能通过完成任务赢取丰厚奖励&#xff01;&#x1f30a; 什么是 Walrus&#xff1f; 数据主权如今正成为越…

git 常用命令 git archive

git archive 是 Git 中用于创建一个包含指定提交或分支中所有文件的归档文件&#xff08;如 .tar 或 .zip&#xff09;的命令。这个命令非常适合用于分发项目快照、备份代码库或导出特定版本的文件。 git archive --formatzip --outputproject.zip HEAD …

Excel 技巧15 - 在Excel中抠图头像,换背景色(★★)

本文讲了如何在Excel中抠图头像&#xff0c;换背景色。 1&#xff0c;如何在Excel中抠图头像&#xff0c;换背景色 大家都知道在PS中可以很容易抠图头像&#xff0c;换背景色&#xff0c;其实Excel中也可以抠简单的图&#xff0c;换背景色。 ※所用头像图片为百度搜索&#x…

持续升级《在线写python》小程序的功能,文章页增加一键复制功能,并自动去掉html标签

增加复制按钮后的界面是这样的 代码如下&#xff1a; <template><view><x-header></x-header><view class"" v-if"article_info"><view class"kuai bgf"><view class"ac fs26"><img sr…

FPGA与ASIC:深度解析与职业选择

IC&#xff08;集成电路&#xff09;行业涵盖广泛&#xff0c;涉及数字、模拟等不同研究方向&#xff0c;以及设计、制造、封测等不同产业环节。其中&#xff0c;FPGA&#xff08;现场可编程门阵列&#xff09;和ASIC&#xff08;专用集成电路&#xff09;是两种重要的芯片类型…