【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++社区的发展贡献自己的力量。让我们一起在模板和仿函数的道路上不断前行,探索编程的无限可能!

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/711053.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IDEA 配置方法模板无法获取到参数值和返回值(methodParameters()、methodReturnType()获取不到值)

问题现象&#xff1a; 我在 review 同事代码时候&#xff0c;发现方法上有注释&#xff0c;但是注释上又没有方法参数和返回值&#xff0c;这不是IDEA 配置了方法模板就可以自动生成的嘛&#xff0c;我出于好奇去问了下该同事是怎么回事&#xff0c;该同事有点不好意思的说我配…

sslyze一键检查服务器检查服务器的 SSL/TLS 安全性(KALI工具系列二十五)

目录 1、KALI LINUX 简介 2、sslyze工具简介 3、信息收集 3.1 目标主机IP&#xff08;服务器&#xff09; 3.2 KALI的IP 4、操作示例 4.1 扫描主机和端口 4.2 批量扫描 4.3 插件扫描 4.4 输出结果 5、总结 1、KALI LINUX 简介 Kali Linux 是一个功能强大、多才多艺…

pyinstall打包exe报错

1- 报错 Please install pywin32-ctypes. 前提&#xff1a;python安装路径中已经安装了pywin32-ctypes。 运行pyinstaller报错 PyInstaller cannot check for assembly dependencies. Please install pywin32-ctypes. 解决思路&#xff1a; python安装路径下Lib\site-packa…

B3981 [信息与未来 2024] 图灵完备

题目描述 &#xff08;你不需要看懂这张图片&#xff1b;但如果你看懂了&#xff0c;会觉得它很有趣。&#xff09; JavaScript 是一种功能强大且灵活的编程语言&#xff0c;也是现代 Web 开发的三大支柱之一 (另外两个是 HTML 和 CSS)。灵活的 JavaScript 包含“自动类型转换…

gitLab批量下载有权限的项目

前言 参考 https://www.jianshu.com/p/b3d4e5cee835 适用于git私服拉取个人所涉及权限的代码&#xff0c;方便有多个项目权限的人快速拉取自己所有权限的代码。 默认生成目录结构与gitlab一致 步骤一:获取权限你的代码权限文件d 从gitlab私服生成所有你有权限的代码信息 …

三运放仪表放大器通过设置单个电阻器的值来调整增益

从公式 1 中可以看出&#xff0c;我们可以通过调整单个电阻器 R G的值来调整仪表放大器的差分增益。这很重要&#xff0c;因为与电路中的其他电阻器不同&#xff0c; RG的值不需要与任何其他电阻器匹配。 例如&#xff0c;如果我们尝试通过更改 R 5的值来设置增益&#xff0c;…

Echarts图表: 矩形树图都有哪些配置项,一文告诉你

Hello&#xff0c;本期和大家分享矩形树图的配置项&#xff0c;欢迎评论区和贝格前端工场互动交流。 一、矩形树图是什么 矩形树图是ECharts中的一种图表类型&#xff0c;用于展示树形结构的数据。矩形树图通过矩形的大小和位置来表示树形结构中节点的层次关系和数据的大小。…

字典键值对找不到?Python魔法方法__missing__来帮你!

目录 1、__missing__基础介绍 🔍 1.1 什么是__missing__方法 1.2 __missing__在字典子类中的作用 2、场景一:自定义缺失键处理逻辑 🎭 2.1 实现默认值返回 2.2 动态数据加载机制 3、场景二:增强Dict功能 🛠️ 3.1 自动缓存未找到项 3.2 键转换与智能查询 4、场…

GenICam标准(三)

系列文章目录 GenICam标准&#xff08;一&#xff09; GenICam标准&#xff08;二&#xff09; GenICam标准&#xff08;三&#xff09; GenICam标准&#xff08;四&#xff09; GenICam标准&#xff08;五&#xff09; GenICam标准&#xff08;六&#xff09; 文章目录 系列文…

亚马逊测评自养号与机刷的区别

前言&#xff1a; 在亚马逊运营的领域中&#xff0c;经常有人问&#xff1a;测评自养号就是机刷吗&#xff1f;它们两者有什么区别&#xff1f;做自养号太慢、太需要时间了&#xff0c;如果用机刷的话&#xff0c;会不会简单高效一点&#xff1f; 在这篇文章中&#xff0c;我…

CinemachineFreelook相机鬼畜抖动的解决方法

又是培训机构的出品。。。。。。 2020.3.27 发现跟随时抖动的原因了&#xff0c;其实应该按照传统相机的跟随逻辑放在late update里执行&#xff0c;但是由于开发cinemachine的开发组很坑地把cinemachine freelook里的三个轨道Damping值默认给了3&#xff0c;于是相机会以每0.7…

C# Winform 用户控件,扩展控件,自定义控件综合实例

Control类是Windows窗体控件的基类&#xff0c;它提供了在 Windows 窗体应用程序中进行可视显示所需的基础结构&#xff0c;可以通过继承来扩展熟悉的用户控件和现有控件的功能。本列介绍三种不同自定义控件以及怎么创建他们。 自定义控件分类 用户控件&#xff1a;基本控件的…

Locust web性能测试实践

Locust web性能测试实践 Locust 是一个开源的负载测试工具&#xff0c;使用Python语言实现&#xff0c;其简洁、轻量、高效的并发机制基于Gevent协程&#xff0c;可以实现单机模拟生成较高的并发压力。具有分布式和可扩展的特点&#xff0c;能够帮助你评估系统的性能并找到潜在…

GO RACE 测试在低版本GCC上报错误 exit status 0xc0000139

windows机器环境&#xff0c;go程序使用race定位时一运行就报错&#xff0c;写了个example如&#xff1a; 能看到加了race之后就不行了&#xff0c; 搜了一下&#xff0c;git上有个issue&#xff1a; runtime: Race detector causes exit status 0xc0000139 on Windows 11 wi…

html圆盘钟表纯js有解释【搬代码】

结果如图所示&#xff1a; 使用的idear中的html编写 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport…

从入门到高手的99个python案例(2)

51. 列表和数组比较 - 列表通用&#xff0c;NumPy数组高效。 import numpy as np normal_list [1, 2, 3] np_array np.array([1, 2, 3]) print(np_array.shape) # 输出 (3,), 数组有形状信息 52. Python的内置模块datetime - 处理日期和时间。 from datetime import…

cocos creator如何使用cryptojs加解密(及引入方法)

cocos creator如何使用cryptojs加解密&#xff08;及引入方法&#xff09; 如果想转请评论留个言并注明原博 Sclifftop 13805064305 阿浚 cocos creator如何使用cryptojs加解密&#xff08;及引入方法&#xff09; 步骤 获取库 1. npm install crypto-js -g&#xff0c;加不加…

电源小白入门学习11——反激电源电路原理

电源小白入门学习11——反激电源、正激电源 隔离电源变压器介绍反激电源 前面我们学习了BUCK、BOOST、BUCK-BOOST 等各种各样的DCDC变换器&#xff0c;但是他们都有一共同的特点&#xff0c;即能量的传输路径时一个完整的通路&#xff0c;输入与输出之间不存在电气隔离&#xf…

[Java基本语法] 逻辑控制与方法

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏:&#x1f355; Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 &#x1f9c0;线程与…

Qwen-Agent:Qwen2加持,强大的多代理框架 - 函数调用、代码解释器以及 RAG!

✨点击这里✨&#xff1a;&#x1f680;原文链接&#xff1a;&#xff08;更好排版、视频播放、社群交流、最新AI开源项目、AI工具分享都在这个公众号&#xff01;&#xff09; Qwen-Agent&#xff1a;Qwen2加持&#xff0c;强大的多代理框架 - 函数调用、代码解释器以及 RAG&…