当前位置: 首页 > news >正文

c++之模板进阶

在前面的文章中,我们已经简单的了解了模板的使用,在这篇文章中,我们将继续深入探讨模板

1.模板的特化

1.1 概念

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

#include <iostream>
using namespace std;
template <class T>
bool LESS(T left, T right)
{return left < right;
}
class date
{
public:date(int year =2025,int month=2,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);}
private:int _year;int _month;int _day;
};
int main()
{cout << LESS(1, 2) << endl;       // 可以比较,结果正确cout << LESS(2, 1) << endl;       // 可以比较,结果正确//日期类(自定义类型)也可以date d1(2025, 4, 20);date d2(2025, 1, 7);cout << LESS(d1, d2) << endl;return 0;
}

但是,倘若我们要这样比较,结果就不一定正确了,因为这里我们在比较地址,而不是比较内容本身! 

因此,我们要对模板进行特化, 使得上述例子得以正确执行!

1.2 函数模板特化

函数模板的特化步骤:

1. 必须要先有一个基础的函数模板

2. 关键字template后面接一对空的尖括号<>

3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型

4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的      错误。

template <class T>
bool LESS(T left, T right)
{return left < right;
}
template<>
bool LESS<date*>(date* left, date* right)
{return *left < *right;
}
int main()
{date d1(2050, 4, 20);date d2(2022, 1, 7);date* p1 = &d1;date* p2 = &d2;cout << LESS(p1, p2) << endl;return 0;
}

 这样,那个指针的例子便可以通过了!因为其将调用特化之后的版本,而不走原来的模板了!

但是,我们之前就学过函数,这样在写一个函数模板,显得多此一举了,不如直接写一个函数,还省事!

bool LESS(date* d1, date* d2)
{return *d1 < *d2;
}
int main()
{date d1(2002, 4, 20);date d2(2022, 1, 7);date* p1 = &d1;date* p2 = &d2;cout << LESS(p1, p2) << endl;return 0;
}

结论:函数模板不建议特化。

1.3 类模板特化

1.3.1 全特化

全特化即是将模板参数列表中所有的参数都确定化

//总的模板
template <class	T1,class T2>
class rens
{
public:rens() { cout << "rens<t1,t2>" << endl; }
private:T1 _r1;T2 _r2;
};
//全特化
template<>
class rens<int*, int*>
{
public:rens() { cout << "rens<int*,int*>" << endl; }
};
template<>
class rens<int, char>
{
public:rens() { cout << "rens<int, char>" << endl; }
//这个私有写不写都行,个人不喜欢写
private:int r1;char r2;
};
template<>
class rens<char, int>
{
public:rens() { cout << "rens<char,int>" << endl; }
//这个私有写不写都行,个人不喜欢写
private:char r1;int r2;
};
int main()
{rens<int, char> r1;rens<int*,int> r2;rens<char, int> r3;rens<int*, int*> r4;return 0;
}

运行结果如下:

结果是这个的原因也不难理解,编译器在选择模板来编译时,一定回去选择最合适的模板来匹配编译! 

1.3.2 偏特化

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

其有两种表现方式:一种是将模板参数类表中的一部分参数特化(见第一个偏特化例子),另一种是针对模板参数更进一步的条件限制所设计出来的一个特化版本。
 

template<class t1,class t2>
class rens
{
public:rens() { cout << "rens<t1,t2>" << endl; }
private:t1 _d1;t2 _d2;
};
//偏特化,将模板参数类表中的一部分参数特化
template<class T1>
class rens<T1, int>
{
public:rens(){cout << "rens<T1,int>" << endl;}
//这个东西写不写都行
private:T1 _d1;int _d2;
};
//两个参数偏特化为指针类型
template<class T1,class T2>
class rens<T1*, T2*>
{
public:rens() { cout << "rens<T1*, T2*>" << endl; }
};
//两个参数偏特化为引用类型
template<class T1,class T2>
class rens<T1&, T2&>
{
public:rens() { cout << "rens<T1&, T2&>" << endl; }
};
//两个参数偏特化为指针类型和引用类型
template<class T1, class T2>
class rens<T1*, T2&>
{
public:rens() { cout << "rens<T1*,T2&>" << endl; }
};
int main()
{rens<double, int> r1;rens<int, double> r2;rens<int*, int*> r3;rens<int&, int&>r4;rens<int*, int&>r5;return 0;
}

运行结果:

2.模板分离编译

与普通函数不同,带有模板参数的函数的声明和定义不能分开!否则会报错!!

为此,我们的解决方案是将带模板参数的函数的声明的定义都放在.h文件中,如下图所示:

3.应用

 比如我现在有一个vector容器,现在我们想写一个函数来打印它,那应该怎么办呢?我们有如下几种方式:

一、老老实实的写打印函数

#include<vector>
#include<list>void print(const vector<int>& v)
{vector<int>::const_iterator it = v.begin();     //参数是const vector<int>,所以我们的迭代 器也要用const_iteratorwhile (it != v.end()){cout << *it << " " ;++it;}cout << endl;
}
int main()
{vector<int> v1 = { 1,2,3,4,5,6 };print(v1);return 0;
}

这样写优缺点也很明确,优点是这种写法很简单,看过几遍就会写,缺点是过于死板,如果再让你打印一下double类型或者其他类型,你还得辛辛苦苦去写,太浪费时间,(不过可以适用于摸鱼哈,开玩笑请勿当真!) 为此,我们可以在函数里面加一丢丢模板,这样我们就不必在写double的打印函数了!

二、加模板参数的打印函数

#include<vector>
template <class T>
void print(const vector<T>& v)
{
//	typename vector<T>::const_iterator it = v.begin();     //解释一下这里为什么要加上typename:// 因为vector<T>是一个模板类,// 编译器可能无法自动推断出vector<T>::const_iterator是一个类型,// 因此需要typename明确指出auto it = v.begin();                      //推荐用auto,让编译器自动给我们推导类型while (it != v.end()){cout << *it << " ";++it;}cout << endl;
}
int main()
{vector<int> v1 = { 1,2,3,4,5,6 };print(v1);vector<double> v2 = { 2.2,5.6,9.6,8.3,4.5 };print(v2);return 0;
}

这里有两种方法,一个是用typename关键字,另外一个就是引入c++11的auto类型, 使用auto更加便捷和简洁!

现在,你已经可以打印各种类型的vector了,但是,倘若现在突然让你打印list容器呢?而且我们来不及挨个写了,为此我们再改造一下,使之得以实现!

三、设置一个容器模板

#include<vector>
#include<list>
template<class container>
void print(const container& con)
{//typename container::const_iterator it = con.begin();auto it = con.begin();while (it != con.end()){cout << *it << " ";++it;}cout << endl;	
}int main()
{vector<int> v1 = { 1,2,3,4,5,6 };print(v1);vector<double> v2 = { 2.2,5.6,9.6,8.3,4.5 };print(v2);list<int> l1 = { 1,5,6,9,8,7 };print(l1);list<double> l2 = { 1.1,2.2,3.3 };print(l2);return 0;
}

这样,不论我是vector<int>,还是list<double>,我都可以通过container模板将其替代回去,完成打印的操作! 

4. 模板总结

【优点】

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

2. 增强了代码的灵活性

【缺陷】

1. 模板会导致代码膨胀问题,也会导致编译时间变长

2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

相关文章:

c++之模板进阶

在前面的文章中&#xff0c;我们已经简单的了解了模板的使用&#xff0c;在这篇文章中&#xff0c;我们将继续深入探讨模板 1.模板的特化 1.1 概念 通常情况下&#xff0c;使用模板可以实现一些与类型无关的代码&#xff0c;但对于一些特殊类型的可能会得到一些错误的结果&a…...

关于Internet Download Manager(IDM)强制下载合并相关二次开发

目录 前言 强制下载视频 强制合并 迁移下载列表 免责声明 附录 前言 那个下载工具IDM不说了&#xff0c;确实有很多便捷的功能&#xff0c;不过也有一些限制 常见的包括但不限于&#xff1a; 1.无法下载有版权保护的视频&#xff08;不管真假&#xff09; 2.有时候下载…...

鸿蒙HarmonyOS Next 视频边播放边缓存- OhosVideoCache

OhosVideoCache 是一个专为OpenHarmony开发(HarmonyOS也可以用)的音视频缓存库&#xff0c;旨在帮助开发者轻松实现音视频的边播放边缓存功能。以下是关于 OhosVideoCache 的详细介绍&#xff1a; 1. 核心功能 边播放边缓存&#xff1a;将音视频URL传递给 OhosVideoCache 处理后…...

(10) 如何获取 linux 系统上的 TCP 、 UDP 套接字的收发缓存的默认大小,以及代码范例

&#xff08;1&#xff09; 先介绍下后面的代码里要用到的基础函数&#xff1a; 以及&#xff1a; &#xff08;2&#xff09; 接着给出现代版的 读写 socket 参数的系统函数 &#xff1a; 以及&#xff1a; &#xff08;3&#xff09; 给出 一言的 范例代码&#xff0c;获取…...

程序代码篇---项目目录结构HSV掩膜Opencv图像处理

文章目录 前言第一部分&#xff1a;项目目录结构第二部分&#xff1a;HSV提取HSV色调&#xff08;Hue&#xff09;含义取值范围 饱和度&#xff08;Saturation&#xff09;含义取值范围 亮度&#xff08;Value&#xff09;含义取值范围 第三部分&#xff1a;Opencv图像处理1. 读…...

注解与反射基础

注解 概述 注解&#xff08;Annotation&#xff09;&#xff0c;从jdk5.0引入。 作用 不是程序本身&#xff0c;可以对程序作出解释&#xff08;这一点和注释没什么区别&#xff09;可以被其他程序读取 格式 注释是以“注释名”在代码中存在的&#xff0c;还可以添加一些…...

Vue指令v-html

目录 一、Vue中的v-html指令是什么&#xff1f;二、v-html指令与v-text指令的区别&#xff1f; 一、Vue中的v-html指令是什么&#xff1f; v-html指令的作用是&#xff1a;设置元素的innerHTML&#xff0c;内容中有html结构会被解析为标签。 二、v-html指令与v-text指令的区别…...

院校联合以项目驱动联合培养医工计算机AI人才路径探析

一、引言 1.1 研究背景与意义 在科技飞速发展的当下&#xff0c;医疗人工智能作为一个极具潜力的新兴领域&#xff0c;正深刻地改变着传统医疗模式。从疾病的早期诊断、个性化治疗方案的制定&#xff0c;到药物研发的加速&#xff0c;人工智能技术的应用极大地提升了医疗服务…...

CDDIS从2025年2月开始数据迁移

CDDIS 将从 2025 年 2 月开始将我们的网站从 cddis.nasa.gov 迁移到 earthdata.nasa.gov&#xff0c;并于 2025 年 6 月结束。 期间可能对GAMIT联网数据下载造成影响。...

前端 | JavaScript中的reduce方法

1. 什么是reduce reduce 方法是 JavaScript 中数组的重要方法之一&#xff0c;用于对数组中的元素进行累积计算。它接收一个回调函数作为参数&#xff0c;并返回一个最终计算结果。reduce 在许多场景下都非常有用&#xff0c;比如求和、数组扁平化、对象计数、数据转换等。 2…...

【C++】B2124 判断字符串是否为回文

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述输入格式&#xff1a;输出格式&#xff1a;样例&#xff1a; &#x1f4af;方法一&#xff1a;我的第一种做法思路代码实现解析 &#x1f4af;方法二&#xff1a;我…...

人工智能学习(五)之机器学习逻辑回归算法

深入剖析机器学习逻辑回归算法 一、引言 在机器学习领域&#xff0c;逻辑回归是一种极为经典且应用广泛的算法。虽说名字里带有 “回归”&#xff0c;但它主要用于解决分类问题&#xff0c;在医学、金融、互联网等多个领域都发挥着关键作用。例如&#xff0c;在医学上辅助判断…...

Bash 基础与进阶实践指南

目录 Bash 简介与基础基本命令与文件操作权限管理与用户管理重定向与管道变量与环境变量通配符与正则表达式Shell 脚本结构与控制流常用内建命令与技巧文本处理常用命令作业控制与进程管理别名与函数实用技巧与注意事项更多 Bash 进阶话题参考资源 1. Bash 简介与基础 1.1 什…...

基于开源AI智能名片2 + 1链动模式S2B2C商城小程序视角下的个人IP人设构建研究

摘要&#xff1a;本文深入探讨在开源AI智能名片2 1链动模式S2B2C商城小程序的应用场景下&#xff0c;个人IP人设构建的理论与实践。通过剖析个人IP人设定义中的“诉求”“特质”“可感知”三要素&#xff0c;结合该小程序特点&#xff0c;阐述其对个人IP打造的影响与推动作用&…...

基于springboot+vue的航空散货调度系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…...

【C++】B2122 单词翻转

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 &#x1f4af;一、我的做法代码实现&#xff1a;代码解析思路分析 &#x1f4af;二、老师的第一种做法代码实现&a…...

OSCP 渗透测试:网络抓包工具的使用指南

在 OSCP 考试和渗透测试中&#xff0c;网络数据分析是至关重要的技能。无论是嗅探明文密码、分析恶意流量&#xff0c;还是溯源攻击&#xff0c;抓包工具都是我们的得力助手。 本文将介绍 OSI 七层网络模型 及其在网络分析中的作用&#xff0c;并详细讲解 Wireshark 和 tcpdum…...

Android 进程间通信

什么是IPC&#xff1f; Android 进程间通信&#xff08;IPC&#xff0c;Inter-Process Communication&#xff09;是Android操作系统中不同进程间交换数据和资源的一种机制。由于Android是多任务操作系统&#xff0c;每个应用通常运行在自己的进程中&#xff0c;以提高安全性和…...

Kubernetes学习之通过Service访问Pod

一、基础概述 1.当通过deployment等controller动态创建和销毁pod使得每个pod都有自己的ip地址&#xff0c;当controller用新的pod替代发生故障的pod时&#xff0c;新的pod会分配到新的ip地址&#xff0c;那么客户端如何稳定的找到并访问pod提供的服务。 2.创建service service从…...

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.18 对象数组:在NumPy中存储Python对象

2.18 对象数组&#xff1a;在NumPy中存储Python对象 目录 #mermaid-svg-shERrGOBuM2rBzeB {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-shERrGOBuM2rBzeB .error-icon{fill:#552222;}#mermaid-svg-shERrGOBuM2rB…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...