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

C++ 模板进阶

 非类型模板参数
 

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

类型模板参数即:出现在模板参数列表中,跟在class或者typename之后的参数类型名称
非类型模板参数即:用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用(只支持整型作为非类型模板参数)
 

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

template<class T,size_t N>//非类型模板参数N
class Stack
{
private:T _a[N];
};

 模板的特化

概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,简单来说,模板特化就是针对某些类型进行特殊化处理
以日期类为例,日期类的具体实现因为不是此次重点,,就不展示代码了,只需要知道日期类重载了关系运算符

template<class T>
bool Less(T left, T 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; // 可以比较,结果正确,日期类重载了关系运算符d1<d2可以进行比较Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; // 可以比较,结果错误,比较的是两个指针,而不是指针指向的对象return 0;
}

此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化

函数模板特化
 

函数模板的特化步骤:

1 一定要有原模版

2 template后面接一对空的尖括号<>

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

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

注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出
 

如:以下是正确写法

template<class T>
bool Less(const T &left, const T& right)
{return left < right;
}template<>
bool Less<Date*>(Date* const & left, Date*const & right)//对Less函数模板进行特化
{return *left < *right;
}

错误写法:

 正确写法设计的模板特化属实是优点不太适应,与其走模板特化,不如直接将具体比较函数直接给出:

bool Less(Date* left, Date* right)
{return *left < *right;
}

函数模板特化在面对某些比较复杂的形参类型时,就显得不那么美好了,因此建议:函数模板可以不用特化,类模板用特化

类模板特化
 

全特化
 

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

template<class T1,class T2>
class Date
{
public:Date(){cout << "Date<T1,T2>" << endl;}
private:T1 _d1;T2 _d2;
};template<>
class Date<int, char>//类模板全特化
{
public:Date(){cout << "Date<int,char>" << endl;}
private:int _d1;char _d2;
};int main()
{Date<int, int> d1;Date<int, char>d2;return 0;
}

偏特化

偏特化并不仅仅是指特化部分参数,更是对模版参数进一步进行条件限制设计的特化版本

比如对于以下模板类:
 

template<class T1,class T2>
class Date
{
public:Date(){cout << "Date<T1,T2>" << endl;}
private:T1 _d1;T2 _d2;
};

偏特化有以下两种表现方式:
一:部分参数特化

template<class T1>
class Date<T1,int>//偏特化
{
public:Date(){cout << "Date<T1,int>" << endl;}
private:T1 _d1;int _d2;
};

二:对模板参数更进一步条件限制

template<class T1, class T2>
class Date<T1*, T2*>//偏特化为指针类型
{
public:Date() { cout << "Data<T1*, T2*>" << endl; }
private:T1 _d1;T2 _d2;
};template<class T1, class T2>
class Date<T1&,T2&>//偏特化为引用类型
{
public:Date(){cout << "Date<T1&,T2&>" << endl;}
private:T1 _d1;T2 _d2;
};
int main()
{Date<int*, double*>d2;Date<int&, double&> d3;return 0;
}

类模板特化其他实例:

 原类模版Less:

template<class T>
class Less
{
public:bool operator()(const T& x, const T& y){return x < y;}
};int main()
{Date d1(2023, 10, 20);Date d2(2023, 9, 18);Date d3(2023, 10, 21);vector<Date> v1;v1.push_back(d1);v1.push_back(d2);v1.push_back(d3);sort(v1.begin(), v1.end(), Less<Date>());//Date类对象因为重载了关系运算符,所以可以直接比较for (auto e : v1){cout << e << endl;}return 0;
}

若是将vector数组中的存储元素换成Date* 那么排序就会出错,因为比较大小时并非使用指针指向的对象,而是地址

template<class T>
class Less
{
public:bool operator()(const T& x, const T& y){return x < y;}
};int main()
{Date d1(2023, 10, 20);Date d2(2023, 9, 18);Date d3(2023, 10, 21);vector<Date*> v1;v1.push_back(&d1);v1.push_back(&d2);v1.push_back(&d3);sort(v1.begin(), v1.end(), Less<Date*>());for (auto e : v1){cout << *e << endl;}return 0;
}

所以可以对Less类模板进行特化,针对指针类型的比较特殊化处理:

template<class T>
class Less<T*>
{
public:bool operator()(T* x,  T* y){return *x < *y;}
};int main()
{Date d1(2023, 10, 20);Date d2(2023, 9, 18);Date d3(2023, 10, 21);vector<Date*> v1;v1.push_back(&d1);v1.push_back(&d2);v1.push_back(&d3);sort(v1.begin(), v1.end(), Less<Date*>());for (auto e : v1){cout << *e << endl;}return 0;
}

模板分离编译
 

什么是分离编译
 

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

模板的分离编译
 

模板的声明与定义分离开,若是在头文件中进行声明,源文件中完成定义会引发链接错误:

A.h:

template<class T>
T Add(const T& x, const T& y);

A.cpp:

template<class T>
T Add(const T& x, const T& y)
{return x + y;
}

main.cpp:

#include"A.h"int main()
{Add(1, 2);Add(2.3, 6.9);return 0;
}

原因:

预处理阶段头文件展开,在main.cpp中包含了Add函数的声明,所以即使没有Add函数的具体实现,编译阶段(生成汇编代码)也是可以通过的,可以在链接时call Add函数的地址,A.cpp中编译器没有看到Add函数模板的实例化,因此不会生成具体的加法函数,也就没有Add函数的地址

链接时,将多个obj文件合并,并处理地址问题,在main.cpp中调用的Add<int> 与 Add<double> ,编译器在连接时去找寻地址,但是这两个函数没有实例化没有生成具体代码,没有地址,因此链接时报错

解决方法:

法一:将声明和定义放到一个文件 "xxx.hpp" 里面或者xxx.h(推荐做法)

hpp文件表明:既有声明也有定义

因为头文件中既包含了声明也有定义,故而编译时直接根据定义实例化,直接就有了地址,不需要在链接时去寻找地址

A.hpp:

template<class T>
T Add(const T& left, const T& right);//声明template<class T>
T Add(const T& left, const T& right)//定义
{return left + right;
}template<class T>
class Stack
{
public:void push(const T& x);//声明void pop();//声明
private:T* _a = nullptr;int _top = 0;int _capacity = 0;
};template<class T>
void Stack<T>::push(const T& x)//定义
{cout << "void Stack<T>::push(const T& x)" << endl;
}template<class T>
void Stack<T>::pop()//定义
{cout << "void Stack<T>::pop()" << endl;
}

main.cpp:

#include"A.hpp"
int main()
{cout<<Add(1, 2)<<endl ;Stack<int>().push(1);Stack<int>().pop();return 0;
}

 

法二:模板定义的位置显式实例化。这种方法不实用,不推荐使用

模板总结:

优点:

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

缺点:

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

相关文章:

C++ 模板进阶

非类型模板参数 模板参数分为&#xff1a;类型模板参数与非类型模板参数 类型模板参数即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之后的参数类型名称非类型模板参数即&#xff1a;用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模…...

jenkins 安装与使用、用户权限划分

jenkins 安装与使用 安装插件&#xff1a; 开启该插件功能 验证用户管理 创建web01~02 使用web01登录 用户权限划分 安装 Role-Based Strategy 插件后&#xff0c;系统管理 中多了如图下所示的一个功能&#xff0c;用户权限的划分就是靠他来做的 创建角色 重新访问 创建项目…...

Hadoop3教程(三十三):(生产调优篇)慢磁盘监控与小文件归档

文章目录 &#xff08;161&#xff09;慢磁盘监控&#xff08;162&#xff09;小文件归档小文件过多的问题如何对小文件进行归档 参考文献 &#xff08;161&#xff09;慢磁盘监控 慢磁盘&#xff0c;是指写入数据时特别慢的一类磁盘。这种磁盘并不少见&#xff0c;当机器运行…...

物联网知识复习

物联网的内涵和体系结构 物联网的基本内涵 物联网的基本内涵在于物联&#xff0c;物物相连或者物和人相连的互联网。 也就是说&#xff0c;它是要由物主动发起的&#xff0c;物物互联的互联网。 它的第一层意思是说物和物相连&#xff1b;第二层意思是说物和人相连。 物联网的…...

Golang爬虫入门指南

引言 网络爬虫是一种自动化程序&#xff0c;用于从互联网上收集信息。随着互联网的迅速发展&#xff0c;爬虫技术在各行各业中越来越受欢迎。Golang作为一种高效、并发性好的编程语言&#xff0c;也逐渐成为爬虫开发的首选语言。本文将介绍使用Golang编写爬虫的基础知识和技巧…...

1024渗透测试如何暴力破解其他人主机的密码(第十一课)

1024渗透测试如何暴力破解其他人主机的密码(第十一课) 1 crunch 工具 crunch是一个密码生成器&#xff0c;一般用于在渗透测试中生成随机密码或者字典攻击。下面是常见的一些使用方法&#xff1a; 生成密码字典 生成6位数字的字典&#xff1a;crunch 6 6 -t -o dict.txt …...

记录一次线下渗透电气照明系统(分析与实战)

项目地址:https://github.com/MartinxMax/S-Clustr 注意 本次行动未造成任何设备损坏,并在道德允许范围内测试 >ethical hacking< 发现过程 在路途中,发现一个未锁的配电柜,身为一个电工自然免不了好奇心(非专业人士请勿模仿,操作不当的话220V人就直了) 根据照片,简…...

Android ADB 常用命令及详解

Android ADB 常用命令及详解 Android Debug Bridge&#xff08;ADB&#xff09;是 Android 开发工具包&#xff08;SDK&#xff09;的一部分&#xff0c;用于与 Android 设备通信和执行各种任务。无论你是 Android 开发者还是普通用户&#xff0c;了解 ADB 命令是非常有用的&a…...

GO 工程下载依赖操作流程(go mod)

1. 写一个main.go文件 package main import ("fmt""net/http""github.com/ClickHouse/clickhouse-go" ) func main() {fmt.Println("服务启动......")http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Requ…...

[Linux打怪升级之路]-system V共享内存

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 本期学习目标&…...

STM32不使用 cubeMX实现外部中断

这篇文章将介绍如何不使用 cubeMX完成外部中断的配置和实现。 文章目录 前言一、文件加入工程二、代码解析exti.cexti.hmain.c 注意&#xff1a;总结 前言 实验开发板&#xff1a;STM32F103C8T6。所需软件&#xff1a;keil5 &#xff0c; cubeMX 。实验目的&#xff1a;如何不…...

Nautilus Chain 与 Coin98 生态达成合作,加速 Zebec 生态亚洲战略进

目前&#xff0c;行业内首个模块化 Layer3 架构公链 Nautilus Chain 已经上线主网&#xff0c;揭示了模块化区块链领域迎来了全新的进程。在主网上线后&#xff0c;Nautilus Chain 将扮演 Zebec 生态中最重要的底层设施角色&#xff0c;并将为 Zebec APP 以及 Zebec Payroll 规…...

method.isAnnotationPresent(Xxx.class)一直为null

​​​​package com.dj.springtest.aspect;import com.dj.springtest.annotation.RequireRoles; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.s…...

基于CNN实现谣言检测 - python 深度学习 机器学习 计算机竞赛

文章目录 1 前言1.1 背景 2 数据集3 实现过程4 CNN网络实现5 模型训练部分6 模型评估7 预测结果8 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于CNN实现谣言检测 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&am…...

MySQL——七、MySQL备份恢复

MySQL 一、MySQL日志管理1、MySQL日志类型2、错误日志3、通用查询日志4、慢查询日志5、二进制日志5.1 开启日志5.2 二进制日志的管理5.3 日志查看5.4 二进制日志还原数据 二、MySQL备份1、备份类型逻辑备份优缺点 2、备份内容3、备份工具3.1 MySQL自带的备份工具3.2 文件系统备…...

iOS如何实现语音转文字功能?

1.项目中添加权限 Privacy - Speech Recognition Usage Description : 需要语音识别权限才能实现语音转文字功能 2.添加头文件 #import <AVFoundation/AVFoundation.h> #import<Speech/Speech.h> 3.实现语音转文字逻辑: 3.1 根据wav语音文件创建请求 SFSpeechU…...

【下载器篇】获取微软应用商店应用安装包的方法

【下载器篇】获取微软应用商店应用安装包的方法 微软应用商店历史版本应用下载方法&#xff0c;部分历史版本无法搜索到—【蘇小沐】 文章目录 【下载器篇】获取微软应用商店应用安装包的方法1.实验环境 &#xff08;一&#xff09;微软商店的在线链接生成器1、复制该应用的在…...

云安全—集群攻击入口攻与防

0x00 前言 说到云安全肯定不能避免的是集群相关的内容&#xff0c;最出色的就是Kubernetes&#xff0c;也就是k8s。当然docker相关的内容也算是集群的一部分。但是docker容器本身的问题还是归属于容器本身。 0x01 概述 在集群攻击入口处的内容主要为&#xff1a; 应用安全恶…...

“传统”开发与AI开发的区别与联系(更新了GPT3.5的反馈)

1、传统开发的算法和软件整体&#xff0c;也可以看成是一个“大模型”&#xff0c;其中有不同层次的处理&#xff0c;最终能够完成从输入到输出的计算&#xff0c;不过&#xff0c;其中的计算都是人工定义的&#xff0c;一般依赖于研究成果的应用。研究成果在实际中的应用处理。…...

Unity 文字显示动画(2)

针对第一版的优化&#xff0c;自动适配文字大小&#xff0c;TextMeshPro可以拓展各种语言。第一版字母类语言效果更好。 using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.UI;public partial class TextBeat…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

利用ngx_stream_return_module构建简易 TCP/UDP 响应网关

一、模块概述 ngx_stream_return_module 提供了一个极简的指令&#xff1a; return <value>;在收到客户端连接后&#xff0c;立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量&#xff08;如 $time_iso8601、$remote_addr 等&#xff09;&a…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...

SpringAI实战:ChatModel智能对话全解

一、引言&#xff1a;Spring AI 与 Chat Model 的核心价值 &#x1f680; 在 Java 生态中集成大模型能力&#xff0c;Spring AI 提供了高效的解决方案 &#x1f916;。其中 Chat Model 作为核心交互组件&#xff0c;通过标准化接口简化了与大语言模型&#xff08;LLM&#xff0…...

DAY 45 超大力王爱学Python

来自超大力王的友情提示&#xff1a;在用tensordoard的时候一定一定要用绝对位置&#xff0c;例如&#xff1a;tensorboard --logdir"D:\代码\archive (1)\runs\cifar10_mlp_experiment_2" 不然读取不了数据 知识点回顾&#xff1a; tensorboard的发展历史和原理tens…...

MeshGPT 笔记

[2311.15475] MeshGPT: Generating Triangle Meshes with Decoder-Only Transformers https://library.scholarcy.com/try 真正意义上的AI生成三维模型MESHGPT来袭&#xff01;_哔哩哔哩_bilibili GitHub - lucidrains/meshgpt-pytorch: Implementation of MeshGPT, SOTA Me…...

SQL进阶之旅 Day 22:批处理与游标优化

【SQL进阶之旅 Day 22】批处理与游标优化 文章简述&#xff08;300字左右&#xff09; 在数据库开发中&#xff0c;面对大量数据的处理任务时&#xff0c;单条SQL语句往往无法满足性能需求。本篇文章聚焦“批处理与游标优化”&#xff0c;深入探讨如何通过批量操作和游标技术提…...

简单介绍C++中 string与wstring

在C中&#xff0c;string和wstring是两种用于处理不同字符编码的字符串类型&#xff0c;分别基于char和wchar_t字符类型。以下是它们的详细说明和对比&#xff1a; 1. 基础定义 string 类型&#xff1a;std::string 字符类型&#xff1a;char&#xff08;通常为8位&#xff09…...