模板讲解之进阶

在之前的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.mzph.cn/news/662707.shtml

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

相关文章

关于v8垃圾回收机制联想到的知识点

对于值类型b来说&#xff0c;就直接释放了其占用的内存&#xff0c;对于引用类型obj来说&#xff0c;销毁的只是变量obj对堆内存地址 1001 的引用&#xff0c;obj的值 { c: 3 } 依然存在于堆内存中。那么堆内存中的变量如何进行回收呢&#xff1f; V8的垃圾回收策略主要是基于…

怎么录制屏幕视频?让你的视频脱颖而出

随着科技的飞速发展&#xff0c;录制屏幕视频已经成为人们日常学习和工作中不可或缺的技能。无论是制作教程、分享游戏高光时刻&#xff0c;还是保存线上会议的内容&#xff0c;屏幕录制都可以帮助我们更好地传达信息。可是怎么录制屏幕视频呢&#xff1f;本文将介绍两种录制屏…

手把手教你如何将项目发布到Maven中央仓库(附步骤及常见问题解决方法)

手把手教你如何将项目发布到Maven中央仓库(附步骤及常见问题解决方法) 业余时间写了个轻量级的权限控制框架 light-security &#xff0c;并发布到了 Maven 中央仓库。发布时的操作步骤还挺多&#xff0c;我这个记性是记不住的&#xff0c;所以记录一下&#xff0c;便于以后查…

【C++】 C++入门— 基于范围的 for 循环

C 基于范围的for循环1 使用样例2 使用条件3 完善措施 Thanks♪(&#xff65;ω&#xff65;)&#xff89;谢谢阅读&#xff01;下一篇文章见&#xff01;&#xff01;&#xff01; 基于范围的for循环 1 使用样例 使用for循环遍历数组&#xff0c;我们通常这么写&#xff1a; …

3D打印、自动升降超静电机驱动方案TMC2209

TMC2209步进电机驱动芯片介绍 TMC2209是一款用于两相步进电机的超静音电机驱动IC。Trinamic的精密StealthChop波器确保了无噪音运行、最大效率和最佳电机转矩。它的快速电流调节和与SpreadCycle的可选组合允许高度动态运动&#xff0c;同时为无传感器归位添加了StallGuard4。集…

python中的可变与不可变、深拷贝和浅拷贝

个人猜想&#xff08;很遗憾失败了&#xff09; 在硬盘或者系统中存在一个字符集 如果存在硬盘中&#xff0c;那么硬盘出厂的时候他的字符集所占用的空间就已经确定了。 如果存在于系统的话&#xff0c;硬盘应该在出厂的时候为系统设置一个存储系统字符集的地方。在安装系统…

2024/2/4周报

文章目录 摘要Abstract文献阅读题目引言创新点方法利用长短期记忆网络学习时空演化特征构建用于气象辅助信息编码的堆叠自编码器使用多任务学习发现全市通用模式 模型实验数据集评估准则实验结果 深度学习Self-attentionself-Attention由来self-attention原理self attention代码…

解锁影视制作新境界:DaVinci Resolve Studio 18引领行业变革

随着科技的不断发展&#xff0c;影视制作行业也在日新月异地变革。在这一进程中&#xff0c;DaVinci Resolve Studio 18以其卓越的性能和无限的创新力&#xff0c;成为了行业的领跑者。 DaVinci Resolve Studio 18是一款集剪辑、调色、音频处理和特效合成于一身的专业级影视制…

康姿百德床垫价格合理功效好,用科技力量守护您的睡眠健康

现代生活中&#xff0c;优质睡眠的观念已深入人心。人们渐渐认识到&#xff0c;一个舒适的床垫不仅仅是睡眠的工具&#xff0c;更是健康的守护者。很多朋友在选购床垫一掷千金&#xff0c;却找不到一款合适的床垫。康姿百德床垫是专为提升睡眠质量研发的床垫&#xff0c;成为了…

保姆级教程:从0到1搭建web自动化测试环境

之前都是在linux上安装&#xff0c;第一次在windows上配置环境&#xff0c;加上距离上次配置环境有点久了&#xff0c;竟也花了点时间。特此记录下保姆级教程&#xff0c;给初学者一个有效的参考&#xff01; 一. 环境搭建 工具清单 工具工具名版本Java开发工具包JDK1.8浏览…

Linux 命令 —— top

Linux 命令 —— top 相对于 ps 是选取一个时间点的进程状态&#xff0c;top 则可以持续检测进程运行的状态。使用方式如下&#xff1a; 用法&#xff1a; top [-d secs] | [-p pid] 选项与参数&#xff1a; -d secs&#xff1a;整个进程界面更新 secs 秒。默认是 5 5 5 秒。…

C++之平衡二叉搜索树查找

个人主页&#xff1a;[PingdiGuo_guo] 收录专栏&#xff1a;[C干货专栏] 大家好&#xff0c;我是PingdiGuo&#xff0c;今天我们来学习平衡二叉搜索树查找。 目录 1.什么是二叉树 2.什么是二叉搜索树 3.什么是平衡二叉搜索树查找 4.如何使用平衡二叉搜索树查找 5.平衡二叉…

一个好看的底部导航栏效果

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>底部导航栏</title> </head> <style&…

Rust学习之Features

Rust学习之Features 一 什么是 Features二 默认 feature三 简单的features应用示例四 可选(optional)的依赖五 依赖的特性5.1 在依赖表中指定5.2 在features表中指定 六 命令行中特性控制七 特性统一路径八 其它8.1 相互排斥特性8.2 观察启用特性8.3 [Feature resolver version…

从源码角度透视QTcpServer:解构QTcpServer的底层原理与技术细节

深入了解QTcpServer的底层原理和技术细节 一、背景二、QTcpServer的基本原理2.1、TCP协议简介2.2、QTcpServer的概念 三、QTcpServer源码解析3.1、QTcpServer的构造函数3.2、调用listen函数启动tcpserver3.3、QSocketNotifier的实现 总结 一、背景 QTcpServer是Qt网络模块中的…

docker 容器指定主机网段

docker 容器指定主机网段。 直接连接到物理网络&#xff1a;使用macvlan技术可以让Docker容器直接连接到物理网络&#xff0c;而不需要通过NAT或端口映射的方式来访问它们。可以提高网络性能和稳定性&#xff0c;同时也可以使容器更加透明和易于管理。 1、首先需要查询网卡的…

vuex store,mutations,getters,actions

文章目录 1.vuex概述2.构建vuex【多组件数据共享】环境Son1.vueSon2.vueApp.vue 3.创建一个空仓库4.如何提供&访问vuex的数据①核心概念 - state状态1.通过store直接访问2.通过辅助函数简化代码 ②核心概念 - mutations&#xff08;粗略&#xff09; 5.核心概念 - mutation…

Leetcode29-最大频率元素计数(3005)

1、题目 给你一个由 正整数 组成的数组 nums 。 返回数组 nums 中所有具有 最大 频率的元素的 总频率 。 元素的 频率 是指该元素在数组中出现的次数。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,2,3,1,4] 输出&#xff1a;4 解释&#xff1a;元素 1 和 2 的频率为 …

python3基础学习一

打印print()函数 R 主要的原因是为防止转义&#xff0c;保证r后面的内容全文输出f “主要作用就是格式化字符串&#xff0c;加上f以后&#xff0c;{“变量/表达式”},花括号里的变量和表达式就可以使用了” def ptSubstr():msg "hello,world!"print(msg[0:-1]);prin…

在Django Admin添加快捷方式django-admin-shortcuts

在Django管理主页上添加简单漂亮的快捷方式。 1.安装 pip install django-admin-shortcuts 2在settings.py注册django-admin-shortcuts INSTALLED_APPS [admin_shortcuts,django.contrib.admin,....... ] 3.添加ADMIN_SHORTCUTS设置 ADMIN_SHORTCUTS [ { ti…