C++-function包装器的应用

C++-function包装器的应用

 

目录

 1.什么是 std::function?

2. function 包装器的原型

 3.使用 function 封装不同类型的函数对象

代码分析

 4.实际应用:

 5. bind 绑定:修改参数传递顺序和数量

2.1 使用 bind 绑定修改参数传递顺序

2.2. bind 绑定:指定特定参数

2.3. bind 绑定与类成员函数

总结:😊


😊前言:在现代 C++ 中,std::function 是一个非常有用的工具,它使得函数能够像对象一样传递、存储和调用。随着 C++11 的到来,std::function 被引入到标准库中,成为函数式编程和回调机制的核心组件之一。在这篇博客中,我们将深入探讨 std::function 的工作原理、应用场景及其优缺点。

 1.什么是 std::function

std::function是 C++11 引入的一个模板类,用于封装任何可调用对象如普通函数、Lambda 表达式、函数指针、成员函数指针或函数对象等)。它允许你存储一个可调用对象,并在需要时调用它。这使得我们可以更加灵活地编写代码,特别是在需要传递回调函数或异步任务时,std::function 显得尤为重要。

std::function 是通过类型擦除实现的,它可以在运行时动态地将不同类型的可调用对象转化为统一的接口。简单来说,它允许你用一个通用的对象来代替不同类型的函数或函数指针

2. function 包装器的原型

std::function在头文件<functional>
// 类模板原型如下 template <class T> function; // undefined template <class Ret, class... Args> class function<Ret(Args...)>;

模板参数说明:

Ret: 被调用函数的返回类型Args…:被调用函数的形参。

 3.使用 function 封装不同类型的函数对象

#include <iostream> #include <functional> #include <string> using namespace std; // 普通函数 void func(int n) { cout << "普通函数: " << n << endl; } // 仿函数 struct Func { void operator()(int n) { cout << "仿函数: " << n << endl; } }; // Lambda 表达式 auto lambda = [](int n) { cout << "Lambda 表达式: " << n << endl; }; int main() { // 使用 std::function 封装不同类型的函数 function<void(int)> f; f = func; // 包装普通函数 f(10); f = Func(); // 包装仿函数 f(20); f = lambda; // 包装 Lambda 表达式 f(30); return 0; } 
代码分析

我们定义了三个不同类型的函数:一个普通函数 func一个仿函数 Func一个 Lambda 表达式 lambda

然后,使用std::function<void(int)>来封装这三种不同类型的函数对象。

通过调用包装后的f,我们可以统一的方式执行这些不同的函数对象。(适配器)

这种方式使得我们能够将多种类型的可调用对象统一为一个接口,方便管理和使用。


 4.实际应用:

 150. 逆波兰表达式求值 - 力扣(LeetCode)

 解法一:

class Solution { public: int evalRPN(vector<string>& tokens) { stack<int> st; for(auto& to:tokens) { if(to=="+"||to=="-"||to=="*"||to=="/") { int right=st.top(); st.pop(); int left=st.top(); st.pop(); switch(to[0]) { case '+': st.push(left+right); break; case '-': st.push(left-right); break; case '*': st.push(left*right); break; case '/': st.push(left/right); break; } } else { st.push(stoi(to)); } } return st.top(); } };

解法二: 利用function包装器:

class Solution { public: int evalRPN(vector<string>& tokens) { // 解题思路:操作数入栈,遇到操作符,取两个数计算后,入栈 // 建立映射关系 unordered_map<string, function<int(int, int)>> hash = { {"+", [](int x, int y)->int { return x + y; } }, {"-", [](int x, int y)->int { return x - y; } }, {"*", [](int x, int y)->int { return x * y; } }, {"/", [](int x, int y)->int { return x / y; } }, }; stack<int> s; for(auto str : tokens) { if(str != "+" && str != "-" && str != "*" && str != "/") s.push(stoi(str)); else { // 注意:先获取 y,再获取 x int y = s.top(); s.pop(); int x = s.top(); s.pop(); s.push(hash[str](x, y)); } } return s.top(); } }; 
function作为C++11的一个知识,还是非常好用的。😊

 5. bind 绑定:修改参数传递顺序和数量

bind是 C++ 标准库中的一个函数模板,它允许我们对函数参数进行预先绑定或重新排列,从而生成一个新的可调用对象。bind的强大之处在于,它不仅能够指定某些参数的固定值,还能改变参数传递的顺序,极大地提高了灵活性。

函数原型:

template <class Fn, class... Args> bind (Fn&& fn, Args&&... args); 

 fn 是传递的 函数对象args 是传给函数的 可变参数包,这里使用了 万能引用(引用折叠),使其在进行模板类型推导时,既能引用左值,也能引用右值。

2.1 使用 bind 绑定修改参数传递顺序
#include <iostream> #include <functional> using namespace std; void Func(int a, int b) { cout << "Func: " << a << " " << b << endl; } int main() { // 正常调用 Func(10, 20); // 使用 bind 改变参数顺序 auto RFunc = bind(Func, std::placeholders::_2, std::placeholders::_1); RFunc(10, 20); // 输出: Func: 20 10 return 0; } 

代码分析

bind(Func, std::placeholders::_2, std::placeholders::_1) 通过 placeholders::_1placeholders::_2指定了新的参数顺序,即将原本的第二个参数和第一个参数交换。

当我们调用 RFunc(10, 20) 时,实际上是将 20 作为第一个参数,10 作为第二个参数传递给 Func

这种参数顺序的改变,在一些特定的应用场景下非常有用,特别是在函数签名不一致时,可以方便地进行适配。

2.2. bind 绑定:指定特定参数

bind 还可以用于指定函数的某些参数为固定值,从而减少后续调用时需要传递的参数个数。

示例代码:使用 bind 绑定指定特定参数

#include <iostream> #include <functional> using namespace std; void Func(int a, int b) { cout << "Func: " << a << " " << b << endl; } int main() { // 使用 bind 绑定第一个参数 auto RFunc = bind(Func, 100, std::placeholders::_1); RFunc(20); // 输出: Func: 100 20 return 0; } 

代码分析

我们通过 bind(Func, 100, std::placeholders::_1) 将第一个参数绑定为固定值 100

后续调用时,我们只需要传递第二个参数20bind 会自动将 100 作为第一个参数传递给 Func

2.3. bind 绑定与类成员函数

bind 还可以用于绑定类成员函数。对于普通函数,绑定非常简单,但对于成员函数,我们需要额外注意如何传递类的对象或指针。

示例代码:使用 bind 绑定静态成员函数

#include <iostream> #include <functional> using namespace std; class Test { public: static void funcA(int val) { cout << "静态成员函数 funcA: " << val << endl; } }; int main() { // 使用 bind 绑定静态成员函数 auto RFunc = bind(&Test::funcA, std::placeholders::_1); RFunc(10); // 输出: 静态成员函数 funcA: 10 return 0; } 

代码分析

对于静态成员函数,我们可以直接使用 &Test::funcA 来绑定。

bind会自动处理函数的绑定,并返回一个新的可调用对象 RFunc我们可以使用它来调用函数。

 示例代码:使用 bind 绑定非静态成员函数

#include <iostream> #include <functional> using namespace std; class Test { public: Test(int n) : _n(n) {} void funcB(int val) { cout << "非静态成员函数 funcB: " << val * _n << endl; } private: int _n; }; int main() { Test t(10); // 使用 bind 绑定非静态成员函数 auto RFunc = bind(&Test::funcB, t, std::placeholders::_1); RFunc(5); // 输出: 非静态成员函数 funcB: 50 return 0; } 

 代码分析

对于非静态成员函数,我们需要提供类的对象 t作为参数来绑定。

bind 会将 t&Test::funcB 结合,并生成一个新的可调用对象。

总结:😊

通过 std::functionbind,C++ 提供了强大的函数包装和绑定功能,使得我们能够在不同类型的函数之间进行无缝切换、修改参数传递顺序以及绑定特定参数。这些工具极大地增强了代码的灵活性和可重用性,特别是在需要对多个不同函数进行统一管理时,它们提供了非常便捷的解决方案。在实际开发中,这些技巧不仅能帮助我们提升编程效率,还能让代码更加简洁和优雅。

Read more

Flutter 组件 pair 适配鸿蒙 HarmonyOS 实战:结构化元组治理,构建轻量级双元数据模型与跨层传递架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 pair 适配鸿蒙 HarmonyOS 实战:结构化元组治理,构建轻量级双元数据模型与跨层传递架构 前言 在鸿蒙(OpenHarmony)生态迈向多维数据感知、涉及高频函数返回值传递、两元坐标互操作及复杂状态标识返回的背景下,如何以最轻量化的方式实现数据的“成对化”封装,已成为提升代码整洁度与系统运行效率的“工程润滑剂”。在鸿蒙设备这类强调 AOT 极致性能与低内存开销的环境下,如果应用为了简单的双元数据(如:经纬度、错误码+消息)而动态创建大量繁琐的单次使用类(POJO),由于由于对象头开销与 GC 压力,极易由于由于“类爆炸”导致内存碎片的堆积。 我们需要一种能够支持强类型泛型、具备不可变属性且无需显式类定义的元组治理方案。 pair 为 Flutter 开发者引入了源自 C++ 与 Java 标准库经典语义的“

By Ne0inhk

CentOS 7 安装 MySQL 8.0.45 解决 GPG 密钥不匹配问题(生产环境合规版)

一、文档说明 1.1 适用场景 本文适用于 CentOS 7 系统,在安装 MySQL 8.0.45 社区版(通过 YUM 源方式)时,遇到「GPG 密钥已安装但与包不匹配」报错的生产环境解决方案。 核心报错信息(本文重点解决): The GPG keys listed for the “MySQL 8.0 Community Server” repository are already installed but they are not correct for this package. Check that

By Ne0inhk
Flutter 三方库 appstream 的鸿蒙化适配指南 - 驾驭 Linux 生态元数据规范,打造高性能、标准化、国际化的 OpenHarmony 桌面应用商店分发基石

Flutter 三方库 appstream 的鸿蒙化适配指南 - 驾驭 Linux 生态元数据规范,打造高性能、标准化、国际化的 OpenHarmony 桌面应用商店分发基石

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 appstream 的鸿蒙化适配指南 - 驾驭 Linux 生态元数据规范,打造高性能、标准化、国际化的 OpenHarmony 桌面应用商店分发基石 前言 随着鸿蒙(OpenHarmony)生态向 PC 和平板端的高速扩张,如何为海量的三方软件建立一套标准化的“数字档案”,成了构建应用商店生态的核心痛点。过去,开发者提交应用信息时,往往采用碎片化的 JSON 或自定义文档。这会导致软件分发时详情页展示不一、多语言支持混乱,甚至连基本的截图和版本日志都难以对齐。 为了解决这个问题,我们需要引入一套具备全球化视野的元数据定义标准。appstream 作为 Linux 生态下最重要的应用信息描述规范,能够通过结构化的 XML 标签,精准定义软件的身世、功能和展示资产。适配到鸿蒙平台后,它不仅能让你的重型“鸿蒙私有应用商店”瞬间具备吞金般的解析能力,

By Ne0inhk
【Linux】Linux基本使用和程序部署

【Linux】Linux基本使用和程序部署

🎬 那我掉的头发算什么:个人主页 🔥 个人专栏: 《javaSE》《数据结构》《数据库》《javaEE》 ⛺️待到苦尽甘来日 文章目录 * Linux环境搭建 * 环境搭建方式 * 使用云服务器 * 使用终端软件连接到Linux * Linux常用命令 * ls * pwd * cd * touch * cat * mkdir * rm * cp * mv * tail * vim * grep * ps * netstat * 搭建java部署环境 * apt * JDK * MYSQL * 部署web项目到Linux * 什么是部署 * 环境配置 * 构建项目并打包 * 上传jar包运行程序 * 杀死进程 Linux环境搭建 环境搭建方式 主要有四种: 1. 直接安装在物理机上。但是 Linux 桌面使用起来非常不友好。所以不建议。【不推荐】。 2. 使用虚拟机软件,

By Ne0inhk