模板讲解之进阶

在之前的C++入门的博客中我们就学习到了模板初阶,今天我们来学习模板的进阶,以便于更好地将模板运用到代码中

非类型模板参数

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

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

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

请看下面的代码:
template的<>里面加了一个N=10,它是一个常量,作为模板的一个参数进行使用

namespace bite
{// 定义一个模板类型的静态数组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;};
}

但是这里有两个需要注意的点:
1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
2. 非类型的模板参数必须在编译期就能确认结果。

模板特化

模板特化的概念

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

废话不多说,请看代码:

// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{return left < right;
}
int main()
{cout << Less(1, 2) << endl; // 代码段1Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Less(d1, d2) << endl; // 代码段2Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; // 代码段3return 0;
}

上面代码中的布尔函数Less是一个仿函数,它可以直接用()使用,很方便,在优先级队列中的greater就是用仿函数实现的

代码段1和代码段2都可以进行比较,并且结果符合我们的预期,但是代码段3比较后的结果时错误的,就是因为date*是一个特殊的类型,无法进行正确的比较,这个时候就可以用到模板特化了!

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

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

函数模板特化

函数模板的特化步骤如下:

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

函数模板特化最基础的前提就是需要有一个基础的函数模板,否则不能进行特化,针对于上面的代码我们特化后就可以解决掉代码段3结果不正确的情况:

template<class T>
bool Less(T left, T right)
{return left < right;
}
// 对Less函数模板进行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{return *left < *right;
}
int main()
{cout << Less(1, 2) << endl;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;
}

我们对Less函数进行模板特化后,代码段3就调用特化之后的模板,输出的结果就是正确的了!

但是函数模板特化实际上用的地方不多,一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出。

该种实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化时特别给出,因此函数模板不建议特化。

类模板特化

类模板特化分为两类:
全特化和偏特化

全特化

全特化即是将模板参数列表中所有的参数都确定化。
例如:
下面代码中的代码段1就是类模板的全特化

template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};
//代码段1
template<>
class Data<int, char>
{
public:Data() { cout << "Data<int, char>" << endl; }
private:int _d1;char _d2;
};
void TestVector()
{Data<int, int> d1;//执行语句1Data<int, char> d2;//执行语句2
}

他的运行结果为:
执行语句1直接调用了类模板,执行语句2是调用了类模板的全特化,他的参数类型为int和char,符合全特化的参数类型
在这里插入图片描述

偏特化

偏特化是任何针对模版参数进一步进行条件限制设计的特化版本。

例如对于以下的类模板:

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;
};

参数更进一步的限制

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

//两个参数偏特化为指针类型
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;
};
void test2()
{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);
// 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;
}

一般一个程序要进行运行:有以下几个步骤:
预处理–>编译–>汇编–>链接
具体的讲解请看图(偷的图)
在这里插入图片描述

解决办法
  1. 将声明和定义放到一个文件 “xxx.hpp” 里面或者xxx.h其实也是可以的。推荐使用这种。
  2. 模板定义的位置显式实例化。这种方法不实用,不推荐使用。

大家想要深入地了解一下分离编译扩展阅读:
http://blog.csdn.net/pongba/article/details/19130

模板总结

【优点】

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

【缺陷】

  1. 模板会导致代码膨胀问题,也会导致编译时间变长
  2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

好了,本篇博客的分享到这里就结束了,感谢大家的支持!

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

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

相关文章

功率信号的频谱

目录 1. 前言2. 功率信号的频谱3. 参考资料 1. 前言 知识点1&#xff1a;函数周期性判定定理   假设函数 f ( x ) f(x) f(x) 和函数 g ( x ) g(x) g(x) 均为周期性函数&#xff0c;其最小正周期分别为 T f T_f Tf​ 和 T g T_g Tg​&#xff0c;若 T f / T g T_f/T_g T…

【软件工程】建模工具之开发各阶段绘图——UML2.0常用图实践技巧(功能用例图、静态类图、动态序列图状态图活动图)

更多示例图片可以参考&#xff1a;&#xff08;除了常见的流程图&#xff0c;其他都有&#xff09; 概念&#xff1a;类图 静态&#xff1a;用例图 动态&#xff1a;顺序图&状态图&活动图 1、【面向对象】UML类图、用例图、顺序图、活动图、状态图、通信图、构件图、部…

Linux实验记录:使用vsftpd服务传输文件

前言&#xff1a; 本文是一篇关于Linux系统初学者的实验记录。 参考书籍&#xff1a;《Linux就该这么学》 实验环境&#xff1a; VmwareWorkStation 17——虚拟机软件 RedHatEnterpriseLinux[RHEL]8——红帽操作系统 备注&#xff1a; 为了解决在多样复杂的设备之间解决传…

Mybatis 自定义ResultHandler(结果处理器)正确使用姿势

阅读指引 一、使用场景二、前提条件三、源码对照四、使用示例目标实现方式 一、使用场景 对Mybatis查询的结果集进行统一批量处理&#xff0c;如进行类型转换、数据变更等等。 二、前提条件 必须是查询语句。mapper方法的返回值必须是void&#xff0c;这也就是不能直接从这个…

【Java】SpringBoot集成freemarker|这下总该学会了吧

目录 什么是Freemarker? 常用的java模板引擎还有哪些&#xff1f; 环境搭建&&快速入门 代码层级结构&#xff1a; pom.xml如下 配置文件application.yml 创建实体类 创建模板 创建controller 创建启动类 测试 freemarker基础 基础语法种类 集合指令&#xff08;List和…

有趣的CSS - 输入框选中交互动效

页面效果 此效果主要使用 css 伪选择器配合 html5 required 属性来实现一个简单的输入框的交互效果。 此效果可适用于登录页入口、小表单提交等页面&#xff0c;增强用户实时交互体验。 核心代码部分&#xff0c;简要说明了写法思路&#xff1b;完整代码在最后&#xff0c;可直…

备战蓝桥杯---数据结构与STL应用(入门1)

话不多说&#xff0c;直接看题&#xff1a; 下面为分析&#xff1a;显然&#xff0c;我们要先合并最小的两堆&#xff08;因为他们在后边也得被计算&#xff0c;换句话&#xff0c;我们独立的看&#xff0c;某一堆的体力值为他自己重量*从现在到最后的次数&#xff09; 因此&a…

26元/月起!腾讯云一键自动搭建4核16G幻兽帕鲁服务器

腾讯云无需任何配置自动搭建幻兽帕鲁游戏联机服务器&#xff0c;游戏24小时在线&#xff0c;4核16G游戏联机服务器低至26元/月起&#xff0c;新手小白也能一键搭建属于自己的幻兽帕鲁游戏联机服务器&#xff01; 第一步&#xff1a;购买游戏联机服务器 购买入口&#xff1a;htt…

Vue3 函数式弹窗优化

背景 前面文章介绍了如何使用 函数式弹窗&#xff0c;弹窗方式是挂载在了全局&#xff0c;使用了getCurrentInstance方法拿到了全局上下文&#xff0c;进而拿到挂载的dialog进行弹窗操作。getCurrentInstance现在已被官方Vue API隐藏&#xff0c;为内部使用函数&#xff0c;现…

Ruoyi-Cloud-Plus_Nacos配置服务漏洞CVE-2021-29441_官方解决方法以及_修改源码解决---SpringCloud工作笔记199

CVE-2021-29441 这个漏洞是Nacos的,通过使用postman,直接访问接口: 就可以直接添加nacos的用户 Nacos是Alibaba的一个动态服务发现、配置和服务管理平台。攻击者通过添加Nacos-Server的User-Agent头部将可绕过(nacos.core.auth.enabled=true)鉴权认证,从而进行API操作。 …

React18-模拟列表数据实现基础表格功能

文章目录 分页功能分页组件有两种接口参数分页类型用户列表参数类型 模拟列表数据分页触发方式实现目录 分页功能 分页组件有两种 table组件自带分页 <TableborderedrowKey"userId"rowSelection{{ type: checkbox }}pagination{{position: [bottomRight],pageSi…

c语言:贪吃蛇的实现

目录 贪吃蛇实现的技术前提&#xff1a; Win32 API介绍 控制台程序&#xff08;console&#xff09; 控制台屏幕上的坐标 GetStdHandle GetConsoleCursorInfo CONSOLE_CURSOR_INFO SetConsoleCursorInfo SetConsoleCursorPosition GetAsyncKeyState 宽字符的打印 …