【C++】模板进阶:深入解析模板特化

在这里插入图片描述

C++语法相关知识点可以通过点击以下链接进行学习一起加油!
命名空间缺省参数与函数重载C++相关特性类和对象-上篇类和对象-中篇
类和对象-下篇日期类C/C++内存管理模板初阶String使用
String模拟实现Vector使用及其模拟实现List使用及其模拟实现容器适配器Stack与Queue

本章将深入探讨模板的高级应用,重点解析模板特化的概念和实现方法。

请添加图片描述
Alt
🌈个人主页:是店小二呀
🌈C语言专栏:C语言
🌈C++专栏: C++
🌈初阶数据结构专栏: 初阶数据结构
🌈高阶数据结构专栏: 高阶数据结构
🌈Linux专栏: Linux

🌈喜欢的诗句:无人扶我青云志 我自踏雪至山巅 请添加图片描述

文章目录

  • 一、非类型模板参数
    • 1.1 非类型模板参数介绍
    • 1.2 非类型模板看成半成品
    • 1.3 array的缺点
  • 二、按需实用化
  • 三、模板特化
    • 3.1 模板特化概念
    • 3.2 函数模板特化
    • 3.3 类模板特化
      • 3.3.1 全特化
    • 3.4 偏特化(半特化)
      • 3.4.1 特化参数部分
      • 3.4.2 参数的进一步限制
    • 3.5 类模板和类模板特化调用情况(注意参数对应位置)
  • 四、模板分离编译
  • 五、模板总结

一、非类型模板参数

1.1 非类型模板参数介绍

模板参数分类:类型形参与非类型形参

  • 类型形参:出现在模板参数列表中的,跟在class或者typename之类的参数类型名称
  • 非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当作常量来使用,以便于控制数据

具体实例:

namespace bit
{template<class T, size_t N = 10>class array{public:T& operator[](size_t index){return _array[index];}const T& operator[](size_t index) const{return _array[index];}size_t size() const{return _size;}bool empty() const{return 0 == _size;}private:T _array[N];size_t _size;};void test1(){array<int, 10> a1; //10array<int, 100> a2;	//100}
}

注意事项:

  • 浮点数、类对象以及字符串是不允许作为非类型模板参数的
  • 非类型的模板参数必须在编译就能确认结果

1.2 非类型模板看成半成品

由于非类型的模板参数传参必须在编译就能确认结果,可以将非类型模板看作半成品,实例化结束就是成品

在这里插入图片描述

1.3 array的缺点

虽然array解决数组,对于越界访问的问题进行添加assert进行优化。但是array巨大的缺陷没有解决,可能会出现栈溢的情况,array为静态数组,本身是在栈上开辟空间(栈上空间比堆空间小很多)。而且使用vector不香吗?解决了越界访问和空间上的问题,而且有众多接口!

二、按需实用化

		T& operator[](size_t index){assert(index < N);size(1);return _array[index];}size_t size() const { return _size; }
注意: 这里是没有发生报错的!

在这里插入图片描述

具体说明:

  • 在operator[]函数体存在调用参数不匹配的问题,但是没有检查出来。由于编译器只是大致检查整体框架是否有错误,没有深入检查具体细节。
  • 如果是调用才会实例化,就会细致检查出语法错误,从而发现错误。实例化这个类的时候,会按需实例化(调用哪个成员函数就实例化哪个,指哪打哪)。
  • 根据模板实例化->半成品模板->实例化具体的类或函数->语法编译

三、模板特化

3.1 模板特化概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理。比如:专门用来进行小于比较的函数模板,仿函数。

//函数模板 -- 参数匹配
template<class T>bool Less(T less, T right)
{return left + right;
}int main()
{Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Less(d1, d2) << endl; //可以比较,结果正确Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; //可以比较,结果错误// 调用特化之后的版本,而不走模板生成了return 0;
}

结果反馈:

  • 这两次比较没有出现编译或运行错误,只是关于比较指针的大小结果差强人意
  • 我们需要是比较指针指向的内容的大小,而不是比较指针大小或者说比较地址的大小,地址都分配是随机的,没有可比性

提出解决措施

  • 需要对模板进行特殊化,在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。这里实现个仿函数或者特化。

3.2 函数模板特化

函数模板特化的步骤

  • 必须要先有一个基础的函数模板(容易被忽视)
  • 关键字template后面接一对空的尖括号<>
  • 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  • 函数形参表:必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误

具体实例:

// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
return left < right;
}//对Less函数模板进行特化 
template<>
bool Less<Date*> (Date* left, Date* right)
{return *left + *right;
}
// 调用特化之后的版本,而不走模板生成了
//有现成的,吃现成的;没有现成的,吃自己做的(模板)
cout << Less(p1, p2) << endl;

个人建议:一般情况下,不建议走函数模板。如果遇到参数类型复杂的,不好处理(比如引用)。对此可以采用函数重载,这种实现简单明了,代码的可读性搞,容易书写。(使用场景还是比较少,函数模板更加高效,但是还是不建议)

3.3 类模板特化

3.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; }
};int main()
{Data<int, int> _a1;//走模板Data<int, char> _a2;//走特化
}

在这里插入图片描述

3.4 偏特化(半特化)

偏特化是指任何针对模板参数进一步进行条件限制设计的特化版本**。不一定是特化部分参数,可能是对参数的进一步限制**

3.4.1 特化参数部分

将模板参数类表中一部分参数特化

template<class T1, class T2>class Data{public:Data() {cout<<"Data<T1, T2>" <<endl;}private:T1 _d1;T2 _d2;};
// 将第二个参数特化为int
template <class T1>class Data<T1, int>{public:Data() {cout<<"Data<T1, int>" <<endl;}private:T1 _d1;int _d2;};

3.4.2 参数的进一步限制

偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本

template<class T1, class T2>class Data{public:Data() {cout<<"Data<T1, T2>" <<endl;}private:T1 _d1;T2 _d2;};//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{public:Data() { cout << "Data<T1*, T2*>" << endl; }private:T1 _d1;T2 _d2;
};//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{public:Data(const T1& d1, const T2& d2): _d1(d1), _d2(d2){cout << "Data<T1&, T2&>" << endl;}private:const T1& _d1;const T2& _d2;
};

3.5 类模板和类模板特化调用情况(注意参数对应位置)

void test ()
{Data<double, int> d1; // 调用特化的int版本Data<int, double> d2; // 调用基础的模板Data<int*, int*> d3; // 调用特化的指针版本Data<int&, int&> d4(1, 2); // 调用特化的引用版本
}

在这里插入图片描述

小结:

  • 类模板特化实际上是为了特定类型参数组合提供了制定的实现,可以将类模板特化看作编译器等待实例化的制定实例(如果是偏特化看作是对类模板的部分定制实例)

四、模板分离编译

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

场景分析:

//a.h
template<class T>T Add(const T& left, const T& right);int func(int x, int y);//a.cpp
template<class T>T Add(const T& left, const T& right)
{return left + right;
}int func(int x, int y)
{return x - y;
}//main.cpp
#include "a.h"
int main()
{Add(1, 2);Add(1.0, 2.0);fun(2, 2);return 0;
}

在这里插入图片描述

说明:

  • 编译func函数都有声明,编译,检查以下函数名和参数匹配,没问题暂且可以过了,他的定义在其他.cpp文件,链接的时候再去其他文件找函数地址
  • 模板在a.cpp中出现报错,通过下列两张图片,就很快发现问题了

在这里插入图片描述

说明:

  • main.cpp调用的地方知道使用T成什么类型,但是只有函数声明,没有定义
  • a.cpp 将a.h展开时,不知道这个T到底是什么类型就会报错
  • 两边都没有得到需要的东西

解决办法:

  • 将模板的定义和声明都放在.h文件中就不会出现链接错误
  • 因为.h预处理展开后,实例化模板时,既有声明也有定义,直接实例。编译时,有函数的定义,直接就有地址,不需要链接时再去找

【分离编译扩展阅读】: http://blog.csdn.net/pongba/article/details/19130

五、模板总结

【优点】

  • 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
  • 增加了代码的灵活性

【缺陷】

  • 模板只是把我们需要干的话,交给编译器去干了
  • 模板会导致代码膨胀,也会导致编译时间变长
  • 出现模板编译错误时,错误信息非常凌乱,不易定位错误

以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二呀C++笔记,希望对你在学习C++语言旅途中有所帮助!
请添加图片描述

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

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

相关文章

SQL进阶技巧:如何将字符串数组清洗为简单map结构? | translate + regexp_replace方法

目录 0 场景描述 1 数据准备 2 问题分析 2.1 方法1 特征法-通用解法 2.2 方法2枚举法(不通用) 3 小结 ~~END~~ 如果觉得本文对你有帮助,那么不妨也可以选择去看看我的博客专栏 ,部分内容如下: 数字化建设通关指南专栏原价99,现在活动价29.9,按照阶梯式增长,直到恢…

Linux命令分享 四 (ubuntu 16.04)(vi操作文件)

1、su 切换用户 su - 用户名 切换到该用户并将目录切换至该用户的主目录 **注意该语句执行后需要输入密码&#xff0c;输入密码时终端不回显&#xff08;不会显示你输入的密码&#xff09;&#xff0c;输完直接回车即可 su 用户名 切换用户但不切换目录 su - root su root **注…

c++20 std::format 格式化说明

在标头<format>定义 ()功能很强大&#xff0c;它把字符串当成一个模板&#xff0c;通过传入的参数进行格式化&#xff0c;并且使用大括号‘{}’作为特殊字符代替‘%’。 1、基本用法 &#xff08;1&#xff09;不带编号&#xff0c;即“{}”&#xff08;2&#xff09;带…

2024年【浙江省安全员-C证】考试试卷及浙江省安全员-C证模拟考试题库

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 浙江省安全员-C证考试试卷是安全生产模拟考试一点通总题库中生成的一套浙江省安全员-C证模拟考试题库&#xff0c;安全生产模拟考试一点通上浙江省安全员-C证作业手机同步练习。2024年【浙江省安全员-C证】考试试卷及…

arcgisPro修改要素XY容差

1、在arcgisPro中XY容差的默认值为1个毫米&#xff0c;及0.001米。为了更精细的数据&#xff0c;需要提高这个精度&#xff0c;如何提高呢&#xff1f; 2、如果直接在数据库下新建要素类&#xff0c;容差只能调至0.0002米。所以&#xff0c;需要在数据库下新建要素数据集。 3…

安全工具 | 使用Burp Suite的10个小tips

Burp Suite 应用程序中有用功能的集合 img Burp Suite 是一款出色的分析工具&#xff0c;用于测试 Web 应用程序和系统的安全漏洞。它有很多很棒的功能可以在渗透测试中使用。您使用它的次数越多&#xff0c;您就越发现它的便利功能。 本文内容是我在测试期间学到并经常的主要…

Gradio 中如何让 Chatbot 自动滚动

在 Gradio 中&#xff0c; Chatbot 是对话组件&#xff0c;接受 history 参数&#xff0c;在目前版本中 &#xff08;gradio4.44.0&#xff09;&#xff0c;不支持自动滚动&#xff0c;用起来很不方便&#xff0c;该功能在社区中已经提出了&#xff0c;目前该功能还没有发布。本…

[000-01-008].第05节:OpenFeign特性-重试机制

我的后端学习大纲 SpringCloud学习大纲 1.1.重试机制的默认值&#xff1a; 1.重试机制默认是关闭的&#xff0c;给了默认值 1.2.测试重试机制的默认值&#xff1a; 1.3.开启Retryer功能&#xff1a; 1.修改配置文件YML的配置&#xff1a; 2.新增配置类&#xff1a; packa…

如何用 Helm Chart 安装指定版本的 GitLab Runner?

本分分享如何使用 Helm 来在 Kubernetes 集群上安装极狐GitLab Runner。整体步骤分为&#xff1a;Helm 的安装、vaules.yaml 文件的配置、Runner 的安装、Runner 的测试。 极狐GitLab 为 GitLab 在中国的发行版&#xff0c;中文版本对中国用户更友好。极狐GitLab 支持一键私有…

2024.9最新:CUDA安装,pytorch库安装

目录 一、CUDA安装 1.查看自己电脑适配的CUDA的最高版本 2.安装CUDA 3.检查环境变量是否配置&#xff0c;安装是否成功 二、pytorch库安装 1.pytorch库下载 2.选择合适的版本 3.查看版本 一、CUDA安装 1.查看自己电脑适配的CUDA的最高版本 在命令提示符里输入nvidia-…

股指期货的详细玩法功能与应用解析

股指期货作为一种重要的金融衍生工具&#xff0c;为投资者提供了多样化的投资和风险管理手段。本文将详细探讨股指期货的三大主要功能&#xff1a;风险规避、价格发现和资产配置。 第一&#xff0c;风险规避功能 1.套期保值&#xff1a;股指期货的风险规避功能主要通过套期保值…

pytorch快速入门(一)—— 基本工具及平台介绍

前言 该pytorch学习笔记应该配合b站小土堆的《pytorch深度学习快速入门教程》使用 环境配置&#xff1a;Anaconda Python编译器&#xff1a;pycharm、jupyter 两大法宝函数 dir&#xff08;&#xff09;&#xff1a;知道包中有什么东西&#xff08;函数 / 属性..…

基于SpringBoot的招生宣传管理系统【附源码】

基于SpringBoot的招生宣传管理系统&#xff08;源码L文说明文档&#xff09; 目录 4 系统设计 4.1 系统概述 4.2系统功能结构设计 4.3数据库设计 4.3.1数据库E-R图设计 4.3.2 数据库表结构设计 5 系统实现 5.1管理员功能介绍 5.1.1管理员登录 …

jmeter 录制APP脚本

一、手机 1、修改网络 代理选择手动→填写服务器主机名&#xff08;电脑IP&#xff0c;如&#xff1a;192.1xx.x.xx&#xff09;→服务器端口&#xff08;任意未被占用端口&#xff0c;如&#xff1a;8888&#xff09; 2、安装证书 手机浏览器访问服务器主机名:服务器端口&a…

有关WSL和docker的介绍

目录标题 如何利用在windows上配置docker实现linux和windows容器修改WSL默认安装&#xff08;也就是linux子系统&#xff09;目录到其他盘 如何利用在windows上配置docker实现linux和windows容器 wsl的基本命令&#xff1a;参考网页 docker入门到实践&#xff1a;参考网页 官方…

一个简约的uniapp登录界面,基于uniapp+vue3+uview-plus

uniapp-vue3-template 一个简约的uniapp登录界面&#xff0c;基于uniappvue3uview-plus 页面主要包括&#xff1a;用户登录&#xff0c;手机验证码登录&#xff0c;用户注册&#xff0c;重置密码等页面 登录进去后为空白模板 源码在文末 界面 源码 uniapp登录界面源码

C++速通LeetCode简单第6题-环形链表

快慢指针真的很好用&#xff01; /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/ class Solution { public:bool hasCycle(ListNode *head) {//快慢指针ListNode* fast…

DNS解析域名详解

你有没有想过&#xff0c;当一个url传过来网络对它进行了哪些操作~DNS又是怎样对域名进行解析的~或者我们为什么要用到域名&#xff0c;为什么不直接使用ip地址~ 对于我们而言&#xff0c;面对长串的ip地址&#xff0c;我们更喜欢记忆较短的域名&#xff0c;但是对于路由器来说…

C++比大小游戏

目录 开头程序程序的流程图程序游玩的效果下一篇博客要说的东西 开头 大家好&#xff0c;我叫这是我58。 程序 #include <iostream> #include <Windows.h> using namespace std; int main() {int ir 1;char chparr[2] { 0 };int ip1 0;int ip2 0;int i 1;c…

双指针算法专题(2)

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; 优选算法专题 想要了解双指针算法的介绍&#xff0c;可以去看下面的博客&#xff1a;双指针算法的介绍 目录 611.有效三角形的个数 LCR 1…