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

【C++进阶】模板与仿函数:C++编程中的泛型与函数式编程思想

📝个人主页🌹:Eternity._
⏩收录专栏⏪:C++ “ 登神长阶 ”
🤡往期回顾🤡:栈和队列相关知识
🌹🌹期待您的关注 🌹🌹

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

❀模板进阶

    • 🧩<<仿函数>>
  • 📕1. 仿函数的概念
  • 📚2. 仿函数的用途
    • 🧩<<模板>>
  • 📕1. 非类型模板参数
  • 📚2. 模板的特化
    • ⭐函数模板特化
    • ⭐类模板特化
      • 🌞全特化
      • 🌙偏特化
  • 📜3. 模板分离编译
    • 🍂模板的分离编译
    • 🍁解决方法
  • 📒4. 模板总结
    • 🔥【优点】
    • 💧【缺陷】
  • 📖5. 总结


前言:在C++编程的广阔天地中,模板和仿函数是两大不可或缺的工具。模板以其强大的类型抽象能力,使得代码复用和泛型编程成为可能;而仿函数,则以其函数对象的特性,为算法和容器提供了灵活多变的操作方式。然而,这两者的深入理解和应用,往往需要程序员具备扎实的编程基础和丰富的实践经验

本文我将带领大家走进模板编程的进阶世界,探索仿函数在实际开发中的应用。我们将从模板的基本概念出发,逐步深入到模板的元编程、特化、偏特化等高级话题,同时结合仿函数的定义、使用场景 一起分析

让我们一同踏上这场关于模板进阶与仿函数应用的探索之旅吧!


🧩<<仿函数>>

📕1. 仿函数的概念

概念: 仿函数(functor)是一个编程术语,其核心概念是指通过实现一个特定的类,使得这个类的使用看上去像一个函数

具体来说,仿函数是一个类或结构体,它重载了operator()运算符,从而使得这
个类的对象可以像函数一样被调用


📚2. 仿函数的用途

在我们当前学习的所有知识中,仿函数的用途貌似只涉及到了在STL中的使用,比如我们刚刚了解过的std::priority_queue,还是之前了解过的std::sort可以通过传递仿函数作为参数来指定自定义的比较、排序、映射等操作。这使得STL算法更加灵活和可重用

在这里插入图片描述
优先级队列中的这个排序的操作其实就是通过仿函数比较出来的


仿函数在priority_queue模拟实现中的使用代码示例
建议结合上一期内容阅读

// 仿函数的定义
template<class T>
class Less
{
public:// 重载operator()bool operator()(const T& x, const T& y){return x < y;}
};template<class T>
class Greater
{
public:// 重载operator()bool operator()(const T& x, const T& y){return x > y;}
};// priority_queue的模板参数 -> Compare就代表调用的仿函数
template<class T, class Container = vector<T>, class Compare = Less<T>>

注意:在库里面less表示升序,greater则表示降序,默认情况下使用的时less升序

Sort算法中仿函数的使用代码示例

int main()
{vector<int> v = { 7,4,1,2,8,9,4,5 };sort(v.begin(), v.end());cout << "less: ";for (auto e : v){cout <<  e << " ";}cout << endl;cout << "greater: ";sort(v.begin(), v.end(),greater<int>());for (auto e : v){cout <<  e << " ";}cout << endl;return 0;
}

在这里插入图片描述


仿函数的单独使用你可以就把他想象成一个函数

template<class T>
class Less
{
public:bool operator()(const T& x, const T& y){return x < y;}
};
int main()
{Less<int> L;cout << L(1, 6) << endl;;return 0;
}

总结来说,仿函数是一种强大的编程工具,它允许开发者将功能封装在类中,并通过重载operator()运算符来使这些类的对象具有类似函数的行为。这种灵活性使得仿函数在代码复用、状态保存以及STL算法中使用等方面具有广泛的应用价值


🧩<<模板>>

📕1. 非类型模板参数

模板参数分类类型形参与非类型形参。

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

我们在之前学的所有模板用的基本上都是类型形参,也就是你传什么,他就用什么,而非类型形参则是相当于固定了一个模板参数的类型

类型形参

template<class T, class T>
// ... 其他待添加内容 ...  

非类型形参

template<typename T, size_t N = 10>  
class Array {  
public:  T data[N];  // ... 其他成员函数 ...  
};  int main() {  Array<int, 10> arr; // 创建一个大小为10的整数数组  // ... 使用arr ...  return 0;  
}

注意:

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

📚2. 模板的特化

概念: 模板的特化(Template Specialization):在C++中是一种技术,它允许我们为模板的特定类型或值提供定制化的实现。这种技术对于满足特定需求或提高性能非常有用

模板特化主要可以分为两种类型:类模板特化 ,函数模板特化


⭐函数模板特化

函数模板的特化步骤:

  • 必须要先有一个基础的函数模板
  • 关键字template后面接一对空的尖括号<>
  • 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  • 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
// 基础的函数模板 -> 函数模板,参数匹配
template<class T>
bool Less(const T& x, const T& y)
{return x < y;
}
// 函数模板特化 -> 对Less函数模板进行特化
template<>
bool Less<double>(const double& x, const double& y)
{return x < y;
}
int main()
{cout << Less(1.1, 6.6) << endl; // 调用特化之后的版本,而不走模板生成了cout << Less(1, 6) << endl;return 0;
}

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


⭐类模板特化

类模板特化(Class Template Specialization)是C++模板编程中的一种机制,它允许我们为类模板的特定类型或类型组合提供专门的定义。在默认情况下,类模板会为所有类型提供通用的实现,但有时候,我们可能希望对某些特定的类型提供不同的实现。这时,就可以使用类模板特化来实现


🌞全特化

全特化是针对类模板的所有模板参数提供专门的定义。全特化的语法与类模板的定义类似,但是需要在尖括号中指定具体的类型

类模板全特化代码示例

template<class T>
class pxt 
{
public:void print() {cout << "print()" << endl;}
};// 全特化版本,针对int类型  
template<>
class pxt <int>
{
public:void print() {cout << "print<int>()" << endl;}
};int main() {pxt<int> P;P.print(); // 输出 "print<int>()"  pxt<double> T;T.print(); // 输出 "print()"  return 0;
}

🌙偏特化

偏特化允许我们对类模板的部分模板参数提供专门的定义。这意味着我们可以为模板参数列表中的一部分参数指定具体的类型,而让其他参数保持通用

template<class T1, class T2>
class pxt
{
public:void print(){cout << "print<T1, T2>()" << endl;}
};// 偏特化版本,针对T2为int的情况  
template<class T1>
class pxt <T1, int>
{
public:void print(){cout << "print<T1, int>()" << endl;}
};int main() {pxt<double, int> P;P.print(); // 输出 "print<T1, int>()" pxt<double, double> T;T.print(); // 输出 "print<T1, T2>()"return 0;
}

偏特化能将参数类型特化成不同的类型

// 两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>// 两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>

注意:

  • 类模板特化不能增加新的成员变量,只能对成员函数进行特化
  • 特化的优先级高于通用模板。当存在多个可用的特化版本时,编译器会选择最匹配的特化版本
  • 在编写类模板特化时,要特别注意避免名称冲突和歧义
  • 类模板特化在编译器进行类型推导和实例化时会被考虑,因此它们应该被定义在模板定义所在的同一命名空间内(或者在模板定义之前的某个地方)

📜3. 模板分离编译

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


🍂模板的分离编译

// 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;
}
// 显示实例化(不推荐)
//template
//int Add(const int& left, const int& right);// main.cpp
#include"a.h"
int main()
{Add(1, 2);Add(1.0, 2.0);return 0;
}

在这里插入图片描述
当程序在编译链接时,编译器找到函数模板地址的,这两个函数当时并没有实例化,所以会导致链接时报错


🍁解决方法

如果遇到模板分离编译相关的问题,常见的解决方法有两种:

  • 将声明和定义放到一个文件(如“xxx.hpp”或“xxx.h”)里面。这是推荐的方法,因为它可以避免分离编译带来的潜在问题
  • 在模板定义位置显式实例化。这种方法不实用,通常不推荐使用,因为它可能导致不必要的代码冗余和编译时间增加。

📒4. 模板总结

🔥【优点】

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

💧【缺陷】

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

📖5. 总结

当我们在编程的旅途中深入探索C++的模板和仿函数(Function Objects,也称为函数对象或仿函数对象)时,我们不禁被它们强大的灵活性和表达能力所震撼。模板和仿函数是C++标准库和许多现代编程范式中不可或缺的一部分,它们为我们提供了编写可重用、类型安全且易于维护的代码的强大工具

  • 通过模板,我们可以编写出与类型无关的代码,使得代码更加通用和灵活。无论是容器类、算法还是其他高级抽象,模板都扮演着核心角色。模板进阶的学习不仅仅是理解如何编写模板代码,更重要的是理解如何设计出能够优雅地处理各种类型的模板结构和算法
  • 而仿函数则为我们提供了一种以对象方式表示和操作函数行为的途径。通过重载operator(),我们可以将函数行为封装在类中,从而可以像操作普通对象一样操作函数。这种能力让我们能够在算法和数据结构中更加灵活地运用函数,同时也为我们提供了更多的控制和定制选项

最后我鼓励大家保持对模板和仿函数的学习热情,不断探索和实践它们的强大功能。通过不断的学习和实践,我们不仅能够提升自己的编程技能,还能够为C++社区的发展贡献自己的力量。让我们一起在模板和仿函数的道路上不断前行,探索编程的无限可能!

在这里插入图片描述
谢谢大家支持本篇到这里就结束了,祝大家天天开心!
在这里插入图片描述

相关文章:

【C++进阶】模板与仿函数:C++编程中的泛型与函数式编程思想

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;栈和队列相关知识 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀模板进阶 &#x1f9e9;<&…...

华安保险:核心系统分布式升级,提升保费规模处理能力2-3倍 | OceanBase企业案例

在3月20日的2024 OceanBase数据库城市行的活动中&#xff0c;安保险信息科技部总经理王在平发表了以“保险行业核心业务系统分布式架构实践”为主题的演讲。本文为该演讲的精彩回顾。 早在2019年&#xff0c;华安保险便开始与OceanBase接触&#xff0c;并着手进行数据库的升级…...

佐西卡在美国InfoComm 2024展会上亮相投影镜头系列

6月12日至14日&#xff0c;2024美国视听显示与系统集成展览会将在拉斯维加斯会议中心盛大开幕。这场北美最具影响力的视听技术盛会&#xff0c;将汇集全球顶尖的视听解决方案&#xff0c;展现专业视听电子系统集成、灯光音响等领域的最新技术动态。 在这场科技盛宴中&#xff0…...

【权威出版/投稿优惠】2024年智慧城市与信息化教育国际会议(SCIE 2024)

2024 International Conference on Smart Cities and Information Education 2024年智慧城市与信息化教育国际会议 【会议信息】 会议简称&#xff1a;SCIE 2024 大会时间&#xff1a;点击查看 大会地点&#xff1a;中国北京 会议官网&#xff1a;www.iacscie.com 会议邮箱&am…...

Android 应用程序 ANR 问题分析总结

ANR (Application Not Responding) 应用程序无响应。如果应用程序在UI线程被阻塞太长时间&#xff0c;就会出现ANR&#xff0c;通常出现ANR&#xff0c;系统会弹出一个提示提示框&#xff0c;让用户知道&#xff0c;该程序正在被阻塞&#xff0c;是否继续等待还是关闭。 1、AN…...

爬虫案例:建设库JS逆向

爬虫流程 1. 确定目标网址和所需内容 https://www.jiansheku.com/search/enterprise/ 只是个学习案例&#xff0c;所以目标就有我自己来选择&#xff0c;企业名称&#xff0c;法定代表人&#xff0c;注册资本&#xff0c;成立日期 2. 对目标网站&#xff0c;进行分析 动态…...

基于springboot的酒店管理系统源码数据库

时代的发展带来了巨大的生活改变&#xff0c;很多事务从传统手工管理转变为自动管理。自动管理是利用科技的发展开发的新型管理系统&#xff0c;这类管理系统可以帮助人完成基本的繁琐的反复工作。酒店是出门的必需品&#xff0c;无论出差还是旅游都需要酒店的服务。由于在旺季…...

Web前端开发 - 5 - JavaScript基础

JavaScript 一、JavaScript基础1. JavaScript入门2. 语句3. 数据类型4. 函数5. 对象6. 数组 一、JavaScript基础 1. JavaScript入门 <script> </script> <script type"text/javascript" src"xxx.js"> </script>//单行注释 /* 多…...

程序员之路:塑造卓越职业素养的探索与实践

序章 在这个数字时代&#xff0c;程序员作为技术进步的推动者&#xff0c;不仅需要掌握扎实的技术技能&#xff0c;更需具备高尚的职业素养&#xff0c;以应对日益复杂的行业挑战。职业素养&#xff0c;犹如编程中的“算法”&#xff0c;虽无形却决定着个人发展的效率与质量。本…...

C# Winform 在低DPI创建窗体后,在高DPI运行时,窗体会自动拉伸,导致窗体显示不全

C# Winform 在低DPI创建窗体后&#xff0c;在高DPI运行时&#xff0c;窗体会自动拉伸&#xff0c;导致窗体显示不全&#xff0c; 比如在分辨率为100% 的电脑创建C#项目&#xff0c;当运动到分辨率为125%的电脑运行时&#xff0c;后者运行的窗体会自动拉伸&#xff0c;窗体显示…...

JWT攻击手册(非常详细)零基础入门到精通,收藏这一篇就够了

JSON Web Token&#xff08;JWT&#xff09;对于渗透测试人员而言可能是一种非常吸引人的攻击途径&#xff0c;因为它们不仅是让你获得无限访问权限的关键&#xff0c;而且还被视为隐藏了通往以下特权的途径&#xff1a;特权升级&#xff0c;信息泄露&#xff0c;SQLi&#xff…...

5.所有权

标题 一、概念二、规则三、示例3.1 变量作用域3.2 所有权的移交&#xff08;深拷贝与浅拷贝&#xff09;3.3 函数与所有权3.4 返回值与作用域3.5 引用的使用 四、切片(&str) 一、概念 所有权是Rust的核心特性。所有程序在运行时都必须管理它们使用计算机内存的方式。Rust的…...

RabbitMQ-工作模式(Publish模式Routing模式)

文章目录 发布/订阅&#xff08;Publish/Subscribe&#xff09;交换机临时队列绑定总体代码示例 路由&#xff08;Routing&#xff09;绑定直连交换机多重绑定发送日志订阅总体代码示例 更多相关内容可查看 发布/订阅&#xff08;Publish/Subscribe&#xff09; 构建一个简单的…...

【机器学习算法】期望最大化(EM)算法概述

期望最大化&#xff08;EM&#xff09;算法是一种迭代算法&#xff0c;用于在有未观测变量的情况下&#xff0c;求解概率模型参数的最大似然估计或最大后验估计。以下是对EM算法的原理与应用进行详细地剖析&#xff1a; EM算法原理 E步 - 期望计算&#xff1a;根据当前估计的模…...

【深度学习】数竹签演示软件系统

往期文章列表&#xff1a; 【YOLO深度学习系列】图像分类、物体检测、实例分割、物体追踪、姿态估计、定向边框检测演示系统【含源码】 【深度学习】物体检测/实例分割/物体追踪/姿态估计/定向边框/图像分类检测演示系统【含源码】 【深度学习】YOLOV8数据标注及模型训练方法整…...

Halcon 多相机统一坐标系

小杨说事-基于Halcon的多相机坐标系统一原理个人理解_多相机标定统一坐标系-CSDN博客 一、概述 最近在搞多相机标定等的相关问题&#xff0c;对于很大的场景&#xff0c;单个相机的视野是不够的&#xff0c;就必须要统一到一个坐标系下&#xff0c;因此我也用了4个相机&#…...

Apache Kylin:大数据分析从入门到精通

一、Kylin简介 Apache Kylin是一个分布式数据分析引擎,专为处理海量数据设计,能够在极短时间内对超大规模数据集进行OLAP(Online Analytical Processing)分析。Kylin通过预计算和高效的查询机制,为用户提供秒级的查询响应时间,支持与Hadoop、Hive、HBase等大数据平台无缝…...

SQL Server 2016导入.bak文件到数据库里面步骤

1、打开SSMS管理器 选择数据库 右键 然后点击还原数据库。 2、选择设备 然后点击三个点 找到本地bak文件&#xff0c;然后点击确定 3、点击确定&#xff0c;会自动弹出来一个成功的提示。...

WPF Frame 简单页面切换示例

原理比较简单&#xff0c;但是有个坑&#xff0c;为了使界面能够正确更新&#xff0c;记得使用 INotifyPropertyChanged 接口来实现属性更改通知。 <Window x:Class"PageTest.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation&…...

kafka-生产者监听器(SpringBoot整合Kafka)

文章目录 1、生产者监听器1.1、创建生产者监听器1.2、创建生产者拦截器1.3、发送消息测试1.4、使用Java代码创建主题分区副本1.5、application.yml配置----v1版1.6、屏蔽 kafka debug 日志 logback.xml1.7、引入spring-kafka依赖1.8、控制台日志 1、生产者监听器 1.1、创建生产…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#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…...

【Go语言基础【12】】指针:声明、取地址、解引用

文章目录 零、概述&#xff1a;指针 vs. 引用&#xff08;类比其他语言&#xff09;一、指针基础概念二、指针声明与初始化三、指针操作符1. &&#xff1a;取地址&#xff08;拿到内存地址&#xff09;2. *&#xff1a;解引用&#xff08;拿到值&#xff09; 四、空指针&am…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...