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

【C++进阶(七)】仿函数深度剖析模板进阶讲解

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:C++从入门到精通⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学习C++
  🔝🔝


在这里插入图片描述

模板进阶

  • 1. 前言
  • 2. 仿函数的概念
  • 3. 仿函数的实际用途
  • 4. 模板的非类型模板参数
  • 5. 模板的特化简单介绍
  • 6. 函数模板深度剖析
  • 7. 类模板的特化深度剖析
    • 7.1 模板的全特化
    • 7.2 模板的偏特化
  • 8. 总结以及拓展

1. 前言

C++进阶中关于STL库的初级数据
结构就已经结束了,高阶数据结构如:
二叉搜索树AVL树,红黑树,哈希
等等将在C++高阶讲解.

本章重点:

本篇文章着重讲解仿函数的概念
以及自行实现一个仿函数.模板进阶
中,着重讲解非类型模板参数,模板
的特化还有模板的分离编译


2. 仿函数的概念

仿函数的本质就是一个类,此类中
运算符重载了括号()!所以它使用起来
和函数很相似,就叫做仿函数

在标准库的优先级队列的类模板中
有这样一个缺省参数叫less:

在这里插入图片描述

这个less就是一个仿函数,它会将
优先级队列变成大堆,在算法库的
sort函数默认是升序,其实就是用的less
与less对应的仿函数是greater,greater
可以将优先级队列变成小堆,将sort变成降序

我们可以模仿实现一下less的使用场景:

class Less
{
public:bool operator()(int x,int y){return x<y;}
};
int main()
{Less functor;cout<<functor(1,2);
}

注:1小于2,会返回true,打印1


3. 仿函数的实际用途

首先是使用库中的某些函数时
仿函数能很方便的改变升降序或大小堆

升序写法:

vector<int> v{9,8,7,6};
sort(v.begin(),v.end());
sort(v.begin(),v.end(),less<int>);

降序写法:

vector<int> v{6,7,8,9};
sort(v.begin(),v.end(),greater<int>);

大堆写法:

priority_queue<int> p1;
priority_queue<int,vector<int>,less<int>> p2;

小堆写法:

priority_queue<int,vector<int>,greater<int>> p;

注:优先级队列的适配器参数在仿函数
前面,想要显示传仿函数,先要穿前面的

当然,greater的内部实现和less
只差了一个符号而已,如下:

class Greater
{
public:bool operator()(int x,int y){return x>y;}
};

4. 模板的非类型模板参数

模板参数类型解析:

模板参数分类类型形参与非类型形参
类型形参即:出现在模板参数列表中
跟在class/typename之后的参数类型
非类型形参,就是用一个常量作为
类(函数)模板的一个参数,在类(函数)
模板中可将该参数当成常量来使用

比如:

template<class T,int N = 10>
class test
{T a[N];
};test<int,50> t1;
test<double> t2;

注:N=10是缺省值,没传时默认为10

在这里插入图片描述

讲到这儿就不得不介绍STL中一个不常用的容器了

在这里插入图片描述

array是静态数组
也就是固定大小的顺序容器
使用时,要显示传参N来初始化数组

array属于C++的数组,使用array
时,不管是越界读还是越界写都能
被检测到从而报错,然而使用C语言
的数组时,越界读写不一定会报错


5. 模板的特化简单介绍

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

template<class T>
bool Less(T left, T right)
{return left < right;
}

Less绝对多数情况下都可以正常比较
但是在特殊场景下就得到错误的结果

比如这里我传入指针地址过来
这里的比较就会有问题,我想比较
的是指针指向的内容,然而传入指针
的话会比较指针的地址高低,就和数据无关

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

类模板分为函数模板和类模板


6. 函数模板深度剖析

函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{return left < right;
}
// 对Less函数模板进行特化
template<>
bool Less<int*>(int* left, int* right)
{return *left < *right;//比较指针指向的内容
}

当传参时给函数传了int类型的指针
那么就不会调用第一个函数,而是走
第二个特化的函数,特化也就是特殊处理

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

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

所以实际上函数模板的特化是不常用的


7. 类模板的特化深度剖析

类模板的特化分为全特化和偏特化

7.1 模板的全特化

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

template<class T1, class T2>
class Data
{
public:Data() {cout<<"Data<T1, T2>" <<endl;}
private:T1 _d1;T2 _d2;
};
template<>
class Data<int, char>
{
public:Data() {cout<<"Data<int, char>" <<endl;}
private:int _d1;char _d2;
};
Data<int, int> d1;
Data<int, char> d2;

和函数模板特化一样,特化的部分
要加上template<>作为格式,上面
初始化时,<int,int>类型不会走模板特化
然而<int,char>类型会走模板特化


7.2 模板的偏特化

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

然而偏特化又有两种表现形式:

  • 部分特化
  • 对参数做进一步限制

比如对于上面例子中的模板类做部分特化:

// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:Data() {cout<<"Data<T1, int>" <<endl;}
private:T1 _d1;int _d2;
};

此时,只要第二个参数是int,就会
走偏特化,第二个参数不是int就不走

对上面的类做参数进一步限制:

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

8. 总结以及拓展

补充完仿函数和模板进阶相关知识后
接下来我们将进入继承和多态的学习
继承和多态这部分在校招中考察的很多
请耐心学习~~

对于模板分离编译的拓展:

为什么模板不能分离编译?
模板分离编译问题剖析


🔎 下期预告:C++继承 🔍

相关文章:

【C++进阶(七)】仿函数深度剖析模板进阶讲解

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; 模板进阶 1. 前言2. 仿函数的概念3. 仿函数的实…...

基于SSM的电动车上牌管理系统(有报告)。Javaee项目。

演示视频&#xff1a; 基于SSM的电动车上牌管理系统&#xff08;有报告&#xff09;。Javaee项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring SpringM…...

mstsc无法保存RDP凭据, 100%生效

问题 即使如下两项都打勾&#xff0c;其还是无法保存凭据&#xff0c;特别是连接Ubuntu (freerdp server)&#xff1a; 解决方法 网上多种复杂方法&#xff0c;不生效&#xff0c;其思路是修改后台配置&#xff0c;以使mstsc跟平常一样自动记住凭据。最后&#xff0c;如下的…...

OpenGLES:绘制一个混色旋转的3D球体

效果展示 本篇博文会实现一个混色旋转的3D球体 一.球体解析 前面几篇博文讲解了如何使用OpenGLES实现不同的3D图形 本篇博文讲解怎样实现3D世界的代表图形&#xff1a;一个混色旋转的3D球体 1.1 极限正多面体 如果有学习过我前几篇3D图形绘制的博文&#xff0c;就知道要想…...

Spring AOP 基于注解源码整理

导入配置类 EnableAspectJAutoProxy 注解导入 AspectJAutoProxyRegistrarImportBeanDefinitionRegistrar#registerBeanDefinitions向容器中加入AnnotationAwareAspectJAutoProxyCreatorAnnotationAwareAspectJAutoProxyCreator#initBeanFactory初始化ReflectiveAspectJAdvisor…...

C语言 —— 函数栈帧的创建和销毁

在我们之前学习函数的时候&#xff0c;我们可能有很多困惑? 比如: 局部变量是怎么创建的?为什么局部变量的值是随机值?函数是怎么传参的?传参的顺序是怎样的?形参和实参是什么关系?函数调用是怎么做的?函数调用是结束后怎么返回的? 那么要解决这些问题, 我们就需要知道…...

Appleid苹果账号自动解锁改密(自动解锁二验改密码)

目前该项目能实现以下功能&#xff1a; 多用户使用&#xff0c;权限控制多账号管理账号分享页&#xff0c;支持设置密码、有效期、自定义HTML内容自动解锁与关闭二步验证自动/定时修改密码自动删除Apple ID中的设备代理池与Selenium集群&#xff0c;提高解锁成功率允许手动触发…...

Conflicting peer dependency: eslint@8.50.0

npm install 输出 npm ERR! code ERESOLVE npm ERR! ERESOLVE could not resolve npm ERR! npm ERR! While resolving: vue/eslint-config-standard6.1.0 npm ERR! Found: eslint-plugin-vue8.7.1 npm ERR! node_modules/eslint-plugin-vue npm ERR! dev eslint-plugin-vue…...

Vue3 defineProps使用

MyTag.vue <script setup> import { ref, nextTick, defineProps, defineEmits } from "vue"; const props defineProps({flag: Boolean,title: String, }); // 写成这样也可以 // const props defineProps(["flag", "title"]);const e…...

机器学习7:逻辑回归

一、说明 逻辑回归模型是处理分类问题的最常见机器学习模型之一。二项式逻辑回归只是逻辑回归模型的一种类型。它指的是两个变量的分类&#xff0c;其中概率用于确定二元结果&#xff0c;因此“二项式”中的“bi”。结果为真或假 — 0 或 1。 二项式逻辑回归的一个例子是预测人…...

生活小记-纸张尺寸

A系列纸张&#xff1a; A0&#xff1a;841 x 1189 毫米A1&#xff1a;594 x 841 毫米A2&#xff1a;420 x 594 毫米A3&#xff1a;297 x 420 毫米A4&#xff1a;210 x 297 毫米A5&#xff1a;148 x 210 毫米A6&#xff1a;105 x 148 毫米A7&#xff1a;74 x 105 毫米A8&#xf…...

【MATLAB源码-第41期】基于压缩感知算法的OFDM系统信道估计和LS算法对比仿真。

操作环境&#xff1a; MATLAB 2013b 1、算法描述 压缩感知&#xff08;Compressed Sensing, CS&#xff09;是一种从稀疏或可压缩信号中重构完整信号的数学理论和技术。下面详细介绍压缩感知和它在OFDM信道估计中的应用。 1. 压缩感知基本概念 在传统采样理论中&#xff0…...

优思学院|六西格玛将烹饪和美味提升至极致

最近&#xff0c;我们曾提到一个美国男子如何利用六西格玛来控制糖尿病。这表明六西格玛逐渐被认为是一个不仅可以在工作场所之外使用&#xff0c;尤其不仅限于制造业的系统。 六西格玛的核心理念是改进过程的质量&#xff0c;从而改善最终结果。如果你做了晚餐或尝试了一道新…...

git stash

git stash 是 Git 中一个非常有用的命令&#xff0c;用于临时保存当前工作目录中的修改&#xff0c;以便你可以切换到其他分支或处理其他任务而不丢失你的修改。它的主要用途是&#xff1a; 保存未提交的修改&#xff1a;你可以使用 git stash 命令将未提交的修改&#xff08;包…...

Flink Data Source

Flink Data Source 一、内置 Data Source Flink Data Source 用于定义 Flink 程序的数据来源,Flink 官方提供了多种数据获取方法,用于帮助开发者简单快速地构建输入流,具体如下: 1.1 基于文件构建 1. readTextFile(path):按照 TextInputFormat 格式读取文本文件,并将…...

怒刷LeetCode的第23天(Java版)

目录 第一题 题目来源 题目内容 解决方法 方法一&#xff1a;贪心算法 方法二&#xff1a;动态规划 方法三&#xff1a;回溯算法 方法四&#xff1a;并查集 第二题 题目来源 题目内容 解决方法 方法一&#xff1a;排序和遍历 方法二&#xff1a;扫描线算法 方法…...

Golang 中的调试技巧

掌握有效的策略和工具&#xff0c;实现顺畅的开发 调试是每位开发人员都必须掌握的关键技能。它是识别、隔离和解决代码库中问题的过程。在 Golang 的世界中&#xff0c;掌握有效的调试技巧可以显著提升您的开发工作流程&#xff0c;并帮助您创建更可靠和健壮的应用程序。在本…...

linux 监控内存利用率

监控内存利用率 使用free来分析CPU使用信息 #!/bin/bashDATE$(date %F" "%H:%M)IP$(ifconfig eth0 |awk -F [ :] /inet addr/{print $4}) MAIL"examplemail.com"TOTAL$(free -m |awk /Mem/{print $2})USE$(free -m |awk /Mem/{print $3-$6-$7})FREE$(($TO…...

43 验证二叉搜索树

验证二叉搜索树 理解题意&#xff1a;验证搜索二叉树&#xff1a;中序遍历是升序题解1 递归&#xff08;学习学习&#xff01;&#xff09;题解2 中序遍历&#xff08;保持升序&#xff09; 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个 有效的二叉搜索树。 有…...

深度学习笔记之微积分及绘图

深度学习笔记之微积分及绘图 学习资料来源&#xff1a;微积分 %matplotlib inline from matplotlib_inline import backend_inline from mxnet import np, npx from d2l import mxnet as d2lnpx.set_np()def f(x):return 3 * x ** 2 - 4 * xdef numerical_lim(f, x, h):retur…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

使用LangGraph和LangSmith构建多智能体人工智能系统

现在&#xff0c;通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战&#xff0c;比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

前端中slice和splic的区别

1. slice slice 用于从数组中提取一部分元素&#xff0c;返回一个新的数组。 特点&#xff1a; 不修改原数组&#xff1a;slice 不会改变原数组&#xff0c;而是返回一个新的数组。提取数组的部分&#xff1a;slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...