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

C++中function,bind,lambda

c++11之前,STL中提供了bind1st以及bind2nd绑定器
首先来看一下他们如何使用:
如果我们要对vector中的元素排序,首先会想到sort,比如:

void output(const vector<int> &vec)
{for (auto v : vec) {cout << v << " ";}cout << endl;
}int main() {vector<int> vec;srand(time(nullptr));for (int i = 0; i < 20; i++) {vec.push_back(rand() % 100 + 1);}output(vec);sort(vec.begin(), vec.end());output(vec);//greater 从大到小排序sort(vec.begin(), vec.end(), greater<int>());output(vec);//less 从小到大排序sort(vec.begin(), vec.end(), less<int>());output(vec);return;
}

sort最后一个参数传入的greater或less都被称为函数对象,顾名思义,表现像函数的对象,因为他们的调用都是在后面加上"()"。
其实这是因为他们都重载了operator()。
来看下greater的定义:

template<class _Ty = void>struct greater{	// functor for operator>_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty first_argument_type;_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty second_argument_type;_CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef bool result_type;constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const{	// apply operator> to operandsreturn (_Left > _Right);}};

可以看到确实重载了operator(),需要传入两个参数,所以它还是一个二元函数对象。函数对象的概念至关重要!
那什么时候会用到bind1st、bind2nd呢?
比如我们现在要找到第一个小于70的位置,插入70。
可以使用find_if:

	auto it1 = find_if(vec.begin(), vec.end(),bind1st(greater<int>(), 70));if (it1 != vec.end()) {vec.insert(it1, 70);}

这里使用bind1st(greater<int>(), 70)作为find_if的第三个参数,bind1st的作用,首先,将70绑定到二元函数对象(greater)的第一个参数上,其次,将二元函数对象(greater)转为一元函数对象(因为70已知了),传入到find_if的第三个参数中。这便是他的应用场景,或者还可以使用bind2nd和less的搭配:

	auto it1 = find_if(vec.begin(), vec.end(),bind2nd(less<int>(), 70));if (it1 != vec.end()) {vec.insert(it1, 70);}

关于bind1st(greater(), 70)和bind2nd(less(), 70)的理解
因为我们要找小于70的位置,所以,
对于greater来说,left > right,所以绑定到第一个位置
对于less来说,left < right,所以绑定到第二个位置

理解了绑定器后,再来看看function
function需要一个函数类型进行实例化:

void hello1()
{cout << "hello world!" << endl;
}void hello2(string str)
{cout << str << endl;
}class Test
{
public:void hello(string str) { cout << str << endl; }
};int main() {function<void()> func1 = hello1;//function<void()> func1(hello1);func1();//func1.operator() => hello1();function<void(string)> func2 = hello2;func2("gao");//func2.operator()(string str) => hello2(str);function<int(int, int)> func3 = [](int a, int b) -> int { return a + b; };cout << func3(100, 200) << endl;//通过function调用类的成员方法function<void(Test*, string)> func5 = &Test::hello;func5(&Test(), "call Test::hello!");return 0;
}

对function的调用,实际上是调用了function的()重载,从而调用原函数。上面的例子中可以看到lambda表达式也可以通过function调用。这其实就说明了function的真正用途:保存函数对象的类型,也是对函数对象的封装。这也是它和c语言的函数指针的区别(lambda无法通过函数指针调用)。

现在有这样一个场景:
两(多)个函数,有大部分的代码都是一样的,其中只有一两行代码有不一样的地方,我们可以对这个不一样的地方,使用function做一个抽象,比如:
有两个vector打印函数,一个打印模5=0的元素,一个打印大于10的元素:

void print(vector<int> &number, function<bool(int)> filter) {for (const int &i : number) {if (filter(i)) {cout << i << endl;}}
}print(numbers, [](int i){ return i % 5 == 0; });
print(numbers, [](int i){ return i > 10; });

这样就不用定义两个不同的打印函数了。

关于闭包的概念:
下面是维基百度对于闭包的定义:
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。 这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

简单来说:闭包可以记忆住创建它时候的那些变量。
下面,我们再通过一个例子来说明。
现在,假设我们的需求是:获取一个集合中最小和最大值,并在稍后的时候(可能是另外一个函数中)打印它们。 这里,我们常规的做法通常是:通过一个函数获取集合的最大,最小值,然后保存住,最后在需要的时候访问这两个值,然后打印它们。
这样做就会需要解决:如何保存和传递最大,最小这两个值。
但实际上,这里我们可以考虑用闭包来实现这个功能,让闭包把最大,最小两个值捕获下来,然后在需要的地方调用就可以了。

请看一下下面这段代码:

void getMinMax(vector<int>& number, function<void ()>& printer) {int min = number.front();int max = number.front();for (int i : number) {if (i < min) {min = i;}if (i > max) {max = i;}}printer = [=] () {cout << "min:" <<min<< endl;cout << "max:" << max << endl;};
}

这里,我们通过function<void ()>& printer传递出这个闭包。 然后,在需要的地方,这样即可:

function<void()> printer;
getMinMax(numbers, printer);
......printer();

这里的printer其实是我们前面从getMinMax函数出传出的闭包,这个闭包捕获了min和max。我们直接传递这个闭包给需要的地方使用,而不用传递裸的两个数值,是不是优雅的不少?

bind

/*
c++11 bind绑定器 -> 返回的结果还是一个函数对象
*/void hello(string str) { cout << str << endl; }
int sum(int a, int b) { return a + b; }class Test 
{
public:int sum(int a, int b) { return a + b; }
};int main()
{bind(hello, "hello, bind!")();cout << bind(sum, 10, 20)() << endl;cout << bind(&Test::sum, Test(), 20, 30)() << endl;//参数占位符 绑定器出了语句,无法继续使用bind(hello, placeholders::_1)("hello bind 2!");cout << bind(sum, placeholders::_1, placeholders::_2)(200, 300) << endl;//此处把bind返回的绑定器binder就复用起来了function<void(string)> func1 = bind(hello, placeholders::_1);func1("hello gao");return 0;
}

使用bind和function的线程池例子:

class Thread
{
public://接收一个函数对象,参数都绑定了,所以不需要参数Thread(function<void()> func) : _func(func) {}thread start(){thread t(_func);return t;}
private:function<void()> _func;
};class ThreadPool
{
public:ThreadPool() {}~ThreadPool() {for (int i = 0; i < _pool.size(); i++) {delete _pool[i];}}void startPool(int size){for (int i = 0; i < size; i++) {//成员方法充当线程函数,绑定this指针_pool.push_back(new Thread(bind(&ThreadPool::runInThread, this, i)));}for (int i = 0; i < size; i++) {_handler.push_back(_pool[i]->start());}for (thread &t : _handler) {t.join();}}
private:vector<Thread*> _pool;vector<thread> _handler;void runInThread(int id) {cout << "call runInThread! id:" << id << endl;}
};int main()
{ThreadPool pool;pool.startPool(10);return 0;
}

lambda(匿名函数对象)
lambda表达式的语法

[捕获外部变量](形参列表)->返回值{操作代码};[]:表示不捕获任何外部变量
[=]:表示以传值的方式捕获外部的所有变量
[&]:表示以传引用的方式捕获外部的所有变量
[this]:捕获外部的this指针
[=,&a]: 表示以传值的方式捕获外部的所有变量,但是a变量以传引用的方式捕获
[a,b]:表示以值传递的方式捕获外部变量a和b
[a,&b]:a以值传递捕获,b以引用捕获

使用举例:

int main()
{auto func1 = []()->void {cout << "hello world!" << endl; };func1();//[]:表示不捕获任何外部变量//编译报错/*int a = 10;int b = 20;auto func3 = [](){int tmp = a;a = b;b = tmp;};*///以值传递a,b,lambda实现的重载函数operator()中,是const方法,不能修改成员变量//如果一定要修改,将lambda修饰成mutable,但是这并不会改变a的值,因为这是值传递//int a = 10;//int b = 20;//auto func3 = [a, b]() /*mutable*///{//	int tmp = a;//	a = b;//	b = tmp;//};vector<int> vec;vec.push_back(1);vec.push_back(2);vec.push_back(3);for_each(vec.begin(), vec.end(), [](int a) {cout << a << endl;});return 0;
}
class Data
{
public:Data(int a, int b) : ma(a), mb(b) {}int ma;int mb;
};int main()
{map<int, function<int(int, int)>> caculateMap;caculateMap[1] = [](int a, int b)->int {return a + b; };caculateMap[2] = [](int a, int b)->int {return a - b; };cout << caculateMap[1](1, 2) << endl;//智能指针自定义删除器unique_ptr<FILE, function<void(FILE*)>>ptr1(fopen("data.txt", "w"), [](FILE *pf) { fclose(pf); });//优先队列using FUNC = function<bool(Data&, Data&)>;priority_queue<Data, vector<Data>, FUNC>maxHeap([](Data &d1, Data &d2)->bool{return d1.mb > d2.mb;});maxHeap.push(Data(10, 20));return 0;
}

lambda表达式是如何实现的?
其实是编译器为我们了创建了一个类,这个类重载了(),让我们可以像调用函数一样使用。所以,你写的lambda表达式和真正的实现,是这个样子的:
在这里插入图片描述
而对于捕获变量的lambda表达式来说,编译器在创建类的时候,通过成员函数的形式保存了需要捕获的变量,所以看起来是这个样子:
在这里插入图片描述
似乎也没有什么神奇的地方。但正是由于编译器帮我们实现了细节,使我们的代码变得优雅和简洁了许多。

参考文章:https://paul.pub/cpp-lambda-function-bind/

相关文章:

C++中function,bind,lambda

c11之前&#xff0c;STL中提供了bind1st以及bind2nd绑定器 首先来看一下他们如何使用&#xff1a; 如果我们要对vector中的元素排序&#xff0c;首先会想到sort&#xff0c;比如&#xff1a; void output(const vector<int> &vec) {for (auto v : vec) {cout <&l…...

跟着美团学设计模式(感处)

读了着篇文章之后发现真的是&#xff0c;你的思想&#xff0c;你的思维是真的比比你拥有什么技术要强的。 注 开闭原则 开闭原则&#xff08;Open-Closed Principle&#xff09;是面向对象设计中的基本原则之一&#xff0c;它的定义是&#xff1a;一个软件实体应该对扩展开放…...

2023/8/19 小红书 Java 后台开发面经

项目都做了些什么&#xff0c;怎么实现的用Redis实现了什么&#xff0c;Redis是单线程的吗&#xff0c;Redis是单线程的为什么快&#xff0c;IO多路复用模型具体实现&#xff0c;持久化怎么实现的为什么用Kafka&#xff0c;架构是什么样的&#xff0c;Broker、Topic、Partition…...

基于traccar快捷搭建gps轨迹应用

0. 环境 - win10 虚拟机ubuntu18 - i5 ubuntu22笔记本 - USB-GPS模块一台&#xff0c;比如华大北斗TAU1312-232板 - 双笔记本组网设备&#xff1a;路由器&#xff0c;使得win10笔记本ip&#xff1a;192.168.123.x&#xff0c;而i5笔记本IP是192.168.123.215。 - 安卓 手机 1.…...

【深度学习-图像识别】使用fastai对Caltech101数据集进行图像多分类(50行以内的代码就可达到很高准确率)

文章目录 前言fastai介绍数据集介绍 一、环境准备二、数据集处理1.数据目录结构2.导入依赖项2.读入数据3.模型构建3.1 寻找合适的学习率3.2 模型调优 4.模型保存与应用 总结人工智能-图像识别 系列文章目录 前言 fastai介绍 fastai 是一个深度学习库&#xff0c;它为从业人员…...

Debian10: 安装nut服务器(UPS)

UPS说明&#xff1a; UPS的作用就不必讲了&#xff0c;我选择是SANTAKTGBOX-850&#xff0c;规格为 850VA/510W&#xff0c;可以满足所需&#xff0c;关键是Debian10自带了驱动可以支持&#xff0c;免去安装驱动&#xff0c;将UPS通过USB线连接服务器即可&#xff0c;如下图所示…...

神经网络基础-神经网络补充概念-47-动量梯度下降法

概念 动量梯度下降法&#xff08;Momentum Gradient Descent&#xff09;是一种优化算法&#xff0c;用于加速梯度下降的收敛速度&#xff0c;特别是在存在高曲率、平原或局部最小值的情况下。动量法引入了一个称为“动量”&#xff08;momentum&#xff09;的概念&#xff0c…...

C++11并发与多线程笔记(13) 补充知识、线程池浅谈、数量谈、总结

C11并发与多线程笔记&#xff08;13&#xff09; 补充知识、线程池浅谈、数量谈、总结 1、补充一些知识点1.1 虚假唤醒&#xff1a;1.2 atomic 2、浅谈线程池&#xff1a;3、线程创建数量谈&#xff1a; 1、补充一些知识点 1.1 虚假唤醒&#xff1a; notify_one或者notify_al…...

python高级基础

文章目录 python高级基础闭包修饰器单例模式跟工厂模式工厂模式单例模式 多线程多进程创建websocket服务端手写客户端 python高级基础 闭包 简单解释一下闭包就是可以在内部访问外部函数的变量&#xff0c;因为如果声明全局变量&#xff0c;那在后面就有可能会修改 在闭包中的…...

使用线性回归模型优化权重:探索数据拟合的基础

文章目录 前言一、示例代码二、示例代码解读1.线性回归模型2.MSE损失函数3.优化过程4.结果解读 总结 前言 在机器学习和数据科学中&#xff0c;线性回归是一种常见而重要的方法。本文将以一个简单的代码示例为基础&#xff0c;介绍线性回归的基本原理和应用。将使用Python和Nu…...

亿级短视频,如何架构?

说在前面 在尼恩的&#xff08;50&#xff09;读者社群中&#xff0c;经常指导大家面试架构&#xff0c;拿高端offer。 前几天&#xff0c;指导一个年薪100W小伙伴&#xff0c;拿到字节面试邀请。 遇到一个 非常、非常高频的一个面试题&#xff0c;但是很不好回答&#xff0…...

jenkins pipeline方式一键部署github项目

上篇&#xff1a;jenkins一键部署github项目 该篇使用jenkins pipeline-script一键部署&#xff0c;且介绍pipeline-scm jenkins环境配置 前言&#xff1a;按照上篇创建pipeline任务&#xff0c;结果报mvn&#xff0c;jdk环境不存在&#xff0c;就很疑惑&#xff0c;然后配置全…...

Vue 项目搭建

环境配置 1. 安装node.js 官网&#xff1a;nodejs&#xff08;推荐 v10 以上&#xff09; 官网&#xff1a;npm 是什么&#xff1f; 由于vue的安装与创建依赖node.js&#xff08;JavaScript的运行环境&#xff09;里的npm&#xff08;包管理和分发工具&#xff09;&#xff…...

【NetCore】09-中间件

文章目录 中间件&#xff1a;掌控请求处理过程的关键1. 中间件1.1 中间件工作原理1.2 中间件核心对象 2.异常处理中间件:区分真异常和逻辑异常2.1 处理异常的方式2.1.1 日常错误处理--定义错误页的方法2.1.2 使用代理方法处理异常2.1.3 异常过滤器 IExceptionFilter2.1.4 特性过…...

机器学习深度学习——BERT(来自transformer的双向编码器表示)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——transformer&#xff08;机器翻译的再实现&#xff09; &#x1f4da;订阅专栏&#xff1a;机器学习&am…...

Datawhale Django后端开发入门 Vscode TASK02 Admin管理员、外键的使用

一.Admin管理员的使用 1、启动django服务 使用创建管理员之前&#xff0c;一定要先启动django服务&#xff0c;虽然TASK01和TASK02是分开的&#xff0c;但是进行第二个流程的时候记得先启动django服务&#xff0c;注意此时是在你的项目文件夹下启动的&#xff0c;时刻注意要执…...

【ES5和ES6】数组遍历的各种方法集合

一、ES5的方法 1.for循环 let arr [1, 2, 3] for (let i 0; i < arr.length; i) {console.log(arr[i]) } // 1 // 2 // 32.forEach() 特点&#xff1a; 没有返回值&#xff0c;只是针对每个元素调用func三个参数&#xff1a;item, index, arr &#xff1b;当前项&#…...

学科在线教育元宇宙VR虚拟仿真平台落实更高质量的交互学习

为推动教育数字化&#xff0c;建设全民终身学习的学习型社会、学习型大国&#xff0c;元宇宙企业深圳华锐视点深度融合VR虚拟现实、数字孪生、云计算和三维建模等技术&#xff0c;搭建教育元宇宙平台&#xff0c;为学生提供更加沉浸式的学习体验&#xff0c;提高学习效果和兴趣…...

[python爬虫] 爬取图片无法打开或已损坏的简单探讨

本文主要针对python使用urlretrieve或urlopen下载百度、搜狗、googto&#xff08;谷歌镜像&#xff09;等图片时&#xff0c;出现"无法打开图片或已损坏"的问题&#xff0c;作者对它进行简单的探讨。同时&#xff0c;作者将进一步帮你巩固selenium自动化操作和urllib…...

vue项目预览pdf功能(解决动态文字无法显示的问题)

最近&#xff0c;因为公司项目需要预览pdf的功能&#xff0c;开始的时候找了市面上的一些pdf插件&#xff0c;都能用&#xff0c;但是&#xff0c;后面因为pdf变成了需要根据内容进行变化的&#xff0c;然后&#xff0c;就出现了需要动态生成的文字不显示了。换了好多好多的插件…...

error while updating dependencies: node_modules包资源权限报错 缓存包构建

vue3vite - 解决报错error while updating dependencies:Error:EACCES:permission denied,mkdir ‘x‘&#xff08;系统权限问题&#xff09; 问题说明 在vite vue3项目开发中&#xff0c;出现报错: [vitel error while updating dependencies: Error:EACCES:permission deni…...

从硬开关到软开关:推挽谐振变换器原理与PSIM仿真实战

1. 从经典到谐振&#xff1a;为什么我们需要推挽变换器&#xff1f;在电源设计的工具箱里&#xff0c;推挽变换器&#xff08;Push-Pull Converter&#xff09;绝对算得上是一位“老将”。它的核心思想非常直观&#xff1a;利用一个带中心抽头的变压器&#xff0c;让两个开关管…...

ComfyUI-Inpaint-CropAndStitch:如何用局部修复技术将AI图像处理速度提升100倍

ComfyUI-Inpaint-CropAndStitch&#xff1a;如何用局部修复技术将AI图像处理速度提升100倍 【免费下载链接】ComfyUI-Inpaint-CropAndStitch ComfyUI nodes to crop before sampling and stitch back after sampling that speed up inpainting 项目地址: https://gitcode.com…...

知识图谱嵌入模型全解析:从TransE到RotatE的演进与实战指南

1. 项目概述&#xff1a;为什么我们需要重新审视KGE&#xff1f;在信息爆炸的时代&#xff0c;我们每天都在和“关系”打交道&#xff1a;社交网络中的好友关系、电商平台上的购买关系、学术论文间的引用关系。如何让机器理解这些错综复杂的实体与关系&#xff0c;并从中挖掘出…...

从basicfwd到自定义发包器:手把手教你用DPDK 21.11写一个高性能发包程序

从basicfwd到自定义发包器&#xff1a;DPDK 21.11高性能网络编程实战 在当今云计算和边缘计算蓬勃发展的时代&#xff0c;网络性能优化已成为开发者必须面对的挑战。DPDK&#xff08;Data Plane Development Kit&#xff09;作为高性能数据包处理框架&#xff0c;正在被越来越多…...

告别Ping不通!STM32H7以太网LWIP裸机移植实战:LAN8720硬件连接与软件调试全记录

STM32H7以太网LWIP裸机移植&#xff1a;从硬件连接到软件调试的深度实战指南 当你在深夜的实验室里盯着屏幕上那个顽固的"Request timed out"提示&#xff0c;第十次尝试ping通你的STM32H750开发板时&#xff0c;那种挫败感我深有体会。以太网移植看似简单——连接几…...

如何免费解锁英雄联盟历史回放?ROFL-Player终极解决方案

如何免费解锁英雄联盟历史回放&#xff1f;ROFL-Player终极解决方案 【免费下载链接】ROFL-Player (No longer supported) One stop shop utility for viewing League of Legends replays! 项目地址: https://gitcode.com/gh_mirrors/ro/ROFL-Player 你是否曾因为英雄联…...

CircuitPython库管理全攻略:从手动安装到CircUp工具实战

1. 项目概述&#xff1a;CircuitPython库管理的核心价值 如果你刚开始接触CircuitPython&#xff0c;可能会被一个看似简单的问题绊住&#xff1a;为什么我复制了别人的代码&#xff0c;板子却毫无反应&#xff0c;或者串口里报了一堆看不懂的错误&#xff1f;十有八九&#x…...

用HFSS仿真一个简单的波导:不只是S参数,教你如何动态可视化电场分布(Animate功能详解)

HFSS波导仿真进阶&#xff1a;从S参数到电场动态可视化的深度解析 1. 理解波导仿真中的场可视化价值 在微波工程领域&#xff0c;仿真工具的价值不仅在于获取S参数这样的量化指标&#xff0c;更在于揭示电磁场在结构中的真实分布与动态行为。HFSS作为行业标准的全波电磁仿真软件…...

Vivado仿真避坑指南:从Testbench编写到波形调试的完整实战(以流水灯为例)

Vivado仿真避坑指南&#xff1a;从Testbench编写到波形调试的完整实战&#xff08;以流水灯为例&#xff09; 在FPGA开发中&#xff0c;仿真环节往往是新手工程师最容易"踩坑"的重灾区。明明RTL代码逻辑清晰&#xff0c;下载到板卡却出现异常行为&#xff1b;仿真波…...