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

【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. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

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

相关文章:

【C++】模板(进阶)

本篇我们来介绍更多关于C模板的知识。模板初阶移步至&#xff1a;【C】模板&#xff08;初阶&#xff09; 1.非类型模板参数 1.1 非类型模板参数介绍 模板参数可以是类型形参&#xff0c;也可以是非类型形参。类型形参就是我们目前接触到的一些模板参数。 //类型模板参数 …...

Esxi下虚拟机磁盘类型厚置备改精简置备

Esxi虚拟机磁盘类型厚置备改精简置备 一、esxi报错磁盘不足 1.1、虚拟机报错磁盘不足 1.2、虚拟机磁盘类型 VMware vSphere 中有两种主要类型的虚拟硬盘&#xff1a;精简配置磁盘和厚置备磁盘。 厚置备磁盘有两种分配模型&#xff1a;厚置备延迟置零和厚置备置零。 三者比…...

Element使用表单重置如果不使用prop,重置无法生效

文章目录 为什么需要 prop&#xff1f;示例&#xff1a;使用 prop 的正确方式关键点总结 在 element-ui 的 el-form 组件中&#xff0c; prop 属性是与表单验证和表单字段绑定密切相关的&#xff0c;尤其在使用 resetFields() 重置表单数据时。 如果不使用 prop&#xff0…...

Windows FileZila Server共享电脑文件夹 映射21端口外网连接

我有这样一个使用场景&#xff0c;在外部网络环境下&#xff0c;通过手机便捷地读取存储在电脑上的视频文件。比如在外出旅行、出差&#xff0c;身边没有携带电脑&#xff0c;仅依靠手机设备&#xff0c;就能随时获取电脑里存储的各类视频&#xff0c;无论是学习资料视频、工作…...

MongoDB 备份与恢复综述

目录 一、基本概述 二、逻辑备份 1、全量备份 2、增量备份 3、恢复 三、物理备份 1、cp/tar/fsync 2、WiredTiger 热备份 3、恢复 四、快照备份 一、基本概述 MongoDB 是一种流行的 NoSQL 数据库&#xff0c;它使用文档存储数据&#xff0c;支持丰富的查询语言和索引…...

node.js 文件操作

在 Node.js 中&#xff0c;文件操作主要通过内置的 fs&#xff08;File System&#xff09;模块来实现。 1. 读取文件 const fs require("fs");// 异步读取文件fs.readFile("example.txt", "utf8", (err, data) > {if (err) {console.erro…...

python编程-OpenCV(图像读写-图像处理-图像滤波-角点检测-边缘检测)图像变换

形态变换 图像处理中的形态学操作是处理图像结构的有效方法。以下是一些常见的形态学操作的介绍及其在 OpenCV 中的实现示例。 1. 腐蚀&#xff08;Erosion&#xff09; 腐蚀操作通过消除图像边界来减少图像中的白色区域&#xff08;前景&#xff09;&#xff0c;使物体的边…...

Spark SQL中的from_json函数详解

Spark SQL中的from_json函数详解 在Spark SQL中&#xff0c;from_json是一个用于解析JSON数据的函数&#xff0c;主要用于将JSON格式的字符串解析为结构化的数据&#xff08;即StructType或其他Spark SQL数据类型&#xff09;。这个函数在处理半结构化数据&#xff08;如JSON日…...

【软件架构】软件的十二种架构简介

软件的十二种架构简介 一、软件的12种架构 1. 单体架构 (Monolithic Architecture)2. 分层架构 (Layered Architecture)3. 事件驱动架构 (Event-Driven Architecture)4. 微服务架构 (Microservices Architecture)5. 服务导向架构 (Service-Oriented Architecture, SOA)6. 客户…...

日历热力图,月度数据可视化图表(日活跃图、格子图)vue组件

日历热力图&#xff0c;月度数据可视化图表&#xff0c;vue组件 先看效果&#x1f447; 在线体验https://www.guetzjb.cn/calanderViewGraph/ 日历图简单划分为近一年时间&#xff0c;开始时间是 上一年的今天&#xff0c;例如2024/01/01 —— 2025/01/01&#xff0c;跨度刚…...

Vue 3中导航守卫(Navigation Guard)结合Axios实现token认证机制

在Vue 3中&#xff0c;导航守卫&#xff08;Navigation Guard&#xff09;用于拦截路由的变化&#xff0c;可以在用户访问页面前进行检查。结合Axios进行token认证机制时&#xff0c;我们可以通过导航守卫在路由跳转时&#xff0c;检查用户的认证状态&#xff0c;确保用户有有效…...

【爬虫】使用 Scrapy 框架爬取豆瓣电影 Top 250 数据的完整教程

前言 在大数据和网络爬虫领域&#xff0c;Scrapy 是一个功能强大且广泛使用的开源爬虫框架。它能够帮助我们快速地构建爬虫项目&#xff0c;并高效地从各种网站中提取数据。在本篇文章中&#xff0c;我将带大家从零开始使用 Scrapy 框架&#xff0c;构建一个简单的爬虫项目&am…...

一分钟学习数据安全——白盒加密及安当应用

白盒加密作为一种先进的加密技术&#xff0c;在数据安全、通信安全和信息隐私保护等多个关键领域都有应用。这次的一分钟&#xff0c;让您快速了解一下白盒加密的概念&#xff0c;以及安当产品中的白盒加密应用。 一、什么是白盒加密 简单来说&#xff0c;白盒加密是一种特殊…...

composer安装指定php版本, 忽略平台原因导致的报错

windows下 //composer安装指定php版本, 写出完整的php和composer.phar路径 D:\phpstudy_pro\Extensions\php\php8.1.11nts\php.exe D:\phpstudy_pro\Extensions\composer1.8.5\composer.phar install windows下一些扩展不支持, 如下图, 所以本地composer安装组件时可以忽略 …...

Java 前端详解

Java 前端详解 Java 前端开发主要涉及使用 Java 相关技术和框架来创建用户界面和处理用户交互。虽然 Java 原本是后端开发的主力语言&#xff0c;但它也提供了许多前端开发工具和框架。以下是 Java 前端开发的主要内容和技术栈。 一、Java 前端技术栈 Java Swing 和 AWT AWT (…...

鸿蒙安装HAP时提示“code:9568344 error: install parse profile prop check error” 问题现象

在启动调试或运行应用/服务时&#xff0c;安装HAP出现错误&#xff0c;提示“error: install parse profile prop check error”错误信息。 解决措施 该问题可能是由于应用使用了应用特权&#xff0c;但应用的签名文件发生变化后未将新的签名指纹重新配置到设备的特权管控白名…...

Javaweb之css

css的三种引入方式 1内行式 2.内嵌式 3.外部样式表 内行式和内嵌式 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&quo…...

实施工程师:面试基础宝典

一.运维工程师和实施工程师的区别&#xff1a;工作内容不同、职能不同、工作形式不同 1.工作内容不同&#xff1a; 运维工程师要对公司硬件和软件进行维护。 硬件包括&#xff1a;机房、机柜、网线光纤、PDU、服 务器、网络设备、安全设备等。 实施工程师包括常用操作系统、应…...

react install

react 安装 React 是一个用于构建用户界面的 JavaScript 库。以下是安装 React 的步骤&#xff1a; 使用 Create React App Create React App 是一个官方支持的命令行工具&#xff0c;用于快速搭建 React 应用。 安装 Node.js 和 npm 确保你的计算机上安装了 Node.js 和 npm…...

ElasticSearch DSL查询之排序和分页

一、排序功能 1. 默认排序 在 Elasticsearch 中&#xff0c;默认情况下&#xff0c;查询结果是根据 相关度 评分&#xff08;score&#xff09;进行排序的。我们之前已经了解过&#xff0c;相关度评分是通过 Elasticsearch 根据查询条件与文档内容的匹配程度自动计算得出的。…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...