C++11 lambda表达式和包装器
C++11 lambda表达式和包装器
- 一.lambda表达式
- 1.lambda表达式的引入
- 2.基本语法和使用
- 1.基本语法
- 2.使用
- 1.传值捕捉的错误之处
- 2.传引用捕捉
- 3.lambda表达式的底层原理
- 4.lambda的特殊之处
- 5.lambda配合decltype的新玩法
- 二.function包装器
- 1.概念
- 2.包装函数
- 1.包装普通函数
- 2.包装成员函数
- 3.包装器的另一个应用:统一模板实例化的类型
- 4.小小总结
- 三.bind包装器
- 1.概念
- 2.bind包装器绑定固定参数
- 1.无意义绑定
- 2.绑定固定参数
- 3.bind包装器调整传参顺序
C++11引入的语法当中
除了STL库当中的区别,还有几个很常用的语法
就是我们今天要介绍的lambda表达式和function包装器
(bind包装器不是特别常用,但是我们有些时候也可能会用到,因此我们也介绍一下)
一.lambda表达式
1.lambda表达式的引入
我们之前学习过函数指针和仿函数,它们都是一种回调函数
只不过仿函数的语法更加简单明了
但是仿函数有一个缺点:
实现它必须要定义一个类出来
例如:
我们要实现一个比较算法,对不同的商品按照价格分别升序和降序排序
#include <iostream>
using namespace std;
#include <algorithm>//sort的头文件
#include <vector>
struct Goods
{string _name;//名称double _price;//价格int _evaluate;//评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};//按照价格升序
struct ComparePriceLess
{bool operator()(const Goods& left, const Goods& right){return left._price < right._price;}
};//按照价格降序
struct ComparePriceGreater
{bool operator()(const Goods& left, const Goods& right){return left._price > right._price;}
};int main()
{vector<Goods> v = { { "苹果", 2.5, 5 }, { "香蕉", 3.2, 4 }, { "橙子", 2.9, 3 }, { "菠萝", 1.5, 4 } };sort(v.begin(),v.end(), ComparePriceLess());for (auto& e : v){cout << "商品名称: " << e._name << " 价格 :" << e._price << " 评价:" << e._evaluate << endl;}cout << endl;sort(v.begin(), v.end(), ComparePriceGreater());for (auto& e : v){cout << "商品名称: " << e._name << " 价格 :" << e._price << " 评价:" << e._evaluate << endl;}cout << endl;return 0;
}

我们发现,仅仅是需要分别按照价格升序和降序排序就需要单独写两个类出来
那么如果我又有需求了:分别按照评分升序和降序等等…
那样的话还要再写几个类
而且仿函数的命名上面也有问题,
刚才我们命名的是ComparePriceLess和ComparePriceGreater
如果有人这么命名呢?
Compare1,Compare2…
那样的话代码可读性就很差了
因此C++11引入了lambda表达式来解决这一问题
我们先来用lambda表达式取代一下刚才的仿函数
int main()
{vector<Goods> v = { { "苹果", 2.5, 5 }, { "香蕉", 3.2, 4 }, { "橙子", 2.9, 3 }, { "菠萝", 1.5, 4 } };//按照价格升序sort(v.begin(), v.end(), [](const Goods& left, const Goods& right){return left._price < right._price;});for (auto& e : v){cout << "商品名称: " << e._name << " 价格 :" << e._price << " 评价:" << e._evaluate << endl;}cout << endl;//按照价格降序sort(v.begin(), v.end(), [](const Goods& left, const Goods& right){return left._price > right._price;});for (auto& e : v){cout << "商品名称: " << e._name << " 价格 :" << e._price << " 评价:" << e._evaluate << endl;}cout << endl;return 0;
}

此时我们每次调用sort的时候只需要传入一个lambda表达式
即可指明比较方式,大大简化代码并且提高了代码的可读性
2.基本语法和使用
1.基本语法
//按照价格升序
sort(v.begin(), v.end(), [](const Goods& left, const Goods& right)
{return left._price < right._price;
});
根据这个排序我们可以看出
lambda表达式的底层就是一个匿名函数
该函数无法直接调用,如果想要直接调用,可借助auto将其赋值给一个对象,用那个对象来调用

小例子:

lambda表达式中的捕捉列表可以捕捉上下文中的变量
被捕捉到的变量可以被lambda表达式使用,而且我们可以设置捕捉的方式是传值还是传引用

2.使用
下面我们用lambda表达式实现一下int类型的swap函数
1.传值捕捉的错误之处
int main()
{int a = 1, b = 2;//这里传值捕捉了a和b,所以后面调用myswap时无需传参auto myswap = [a, b](){int tmp = a;a = b;b = tmp;};myswap();cout << "交换后 : " << " a: " << a <<" b: " << b << endl;return 0;
}


所以我们要加上mutable

可是交换之后a还是1,b还是2啊?
为什么?
因为我们是传值捕捉,形参的改变不会影响实参
因此mutable的意义就是提醒我们
1.如果你想要改变实参,请你传引用捕捉,传值捕捉的话改变的是形参不是实参
2.如果你不想改变实参,传值捕捉的话,需要加上mutable提醒别人这里是传值捕捉,不会影响实参
2.传引用捕捉
方案1:传值捕获a和b,参数列表无需传参

方案2:捕获列表不去捕获,参数列表传引用传参

3.lambda表达式的底层原理
其实lambda表达式的底层原理就是仿函数,对operator()进行了重载
就像是范围for的底层原理就是迭代器一样
struct Myswap
{void operator()(int& a, int& b){int tmp = a;a = b;b = tmp;}
};
int main()
{int a = 1, b = 2;//这里捕获列表不去捕获,参数列表需要传参auto myswap = [](int& a,int& b){int tmp = a;a = b;b = tmp;};int x = 9;//为了方便调试打断点看反汇编Myswap myswap2;myswap2(a, b);myswap(a,b);return 0;
}


这是因为每个lambda表达式的类型是不同的
(在VS下,lambda会被处理为函数对象,该函数对象对应的类名叫做<lambda_uuid>)

关于uuid
大家感兴趣的话可以百度一下
UUID

4.lambda的特殊之处
lambda不支持直接调用默认构造来构造对象
但是支持拷贝构造对象


5.lambda配合decltype的新玩法
sort是传入函数对象即可,可是对于priority_queue这种需要传入类的容器来说,lambda就没有用武之地了吗?
并不是的

//建一个小堆(注意:优先级队列:默认是大堆配less,小堆配greater)
auto f = [](int a, int b) {return a > b; };priority_queue<int, vector<int>, decltype(f)> minheap(f);

二.function包装器
1.概念

2.包装函数
下面我们来使用一下function包装器
头文件是<functional>
1.包装普通函数
#include <iostream>
using namespace std;
#include <functional>//1.函数
int add1(int a, int b)
{return a + b;
}//2.仿函数
struct Add2
{int operator()(int a, int b){return a + b;}
};int main()
{//1.包装函数指针function<int(int, int)> f1;f1 = add1;cout << f1(1, 2) << endl;//2.包装仿函数function<int(int, int)> f2 = Add2();//Add2():匿名对象cout << f2(1, 2) << endl;//3.包装lambda表达式function<int(int, int)> f3 = [](const int a, const int b) {return a + b; };cout << f3(1, 2) << endl;return 0;
}

2.包装成员函数


可见:包装器的本质就是对各种可调用对象进行类型的统一
3.包装器的另一个应用:统一模板实例化的类型

//传入该函数模板的第一个参数可以是任意可调用对象:函数指针,仿函数,lambda表达式
//func中定义了静态变量count,每次调用时打印count的值和地址,可以用来判断多次调用时所调用的是否是用一个func函数
template<class A,class T>
T func(A a, T b)
{static int count = 0;cout << "count值: " << ++count << endl;cout << "count地址: " << &count << endl;return a(b);
}//传入第二个参数相同的类型,但是传入的可调用对象的类型是不同的
//那么在编译阶段该模板函数就会被实例化多次
struct Func1
{double operator()(double d){return d / 2;}
};double func2(double d)
{return d / 2;
}int main()
{//函数cout << func(func2, 2.2) << endl;//仿函数cout << func(Func1(), 2.2) << endl;//lambda表达式cout << func([](double d)->double {return d / 2; }, 2.2) << endl;return 0;
}

尽管三次调用传入的可调用对象的类型不同,
但是这三次调用对象的返回值和形参类型相同
因此我们就可以使用包装器来对这三个不同的可调用对象来进行包装,此时就可以只实例化一份func函数了

4.小小总结
function包装器可以统一可调用对象的类型
包装后明确了可调用对象的返回值和形参类型,更加方便使用者进行使用
三.bind包装器
1.概念

头文件:functional
2.bind包装器绑定固定参数
bind绑定可以跟function包装器集合使用

1.无意义绑定
int Add(int a, int b)
{return a + b;
}int main()
{//绑定函数Add,参数分别由调用func1的第一,二参数来指定function<int(int, int)> func1 = bind(Add, placeholders::_1, placeholders::_2);cout << func1(1, 2) << endl;return 0;
}


2.绑定固定参数


3.bind包装器调整传参顺序
我们以Sub类为例


同理,普通函数也是如此



以上就是C++11 lambda表达式和包装器的全部内容,希望能对大家有所帮助!
相关文章:
C++11 lambda表达式和包装器
C11 lambda表达式和包装器 一.lambda表达式1.lambda表达式的引入2.基本语法和使用1.基本语法2.使用1.传值捕捉的错误之处2.传引用捕捉 3.lambda表达式的底层原理4.lambda的特殊之处5.lambda配合decltype的新玩法 二.function包装器1.概念2.包装函数1.包装普通函数2.包装成员函数…...
3. MySQL 数据表的基本操作
文章目录 【 1. MySQL 创建数据表 】【 2. MySQL 查看表 】2.1 查看表的属性DESCRIBE/DESC 以表格的形式展示表属性SHOW CREATE TABLE 以SQL语句的形式展示表属性 2.2 查看表的内容 【 3. MySQL 修改数据表结构 】3.1 修改表名3.2 修改表字符集3.3 添加字段在末尾添加字段在开头…...
Linux命令篇(一):文件管理部分
💝💝💝首先,欢迎各位来到我的博客,很高兴能够在这里和您见面!希望您在这里不仅可以有所收获,同时也能感受到一份轻松欢乐的氛围,祝你生活愉快! 文章目录 1、cat命令常用参…...
IP协议1.0
基本概念: • 主机: 配有IP地址, 但是不进⾏路由控制的设备; • 路由器: 即配有IP地址, ⼜能进⾏路由控制; • 节点: 主机和路由器的统称; IP协议的报头 • 4位版本号(version): 指定IP协议的版本, 对于IPv4来说, 就是4. • 4位头部⻓度(header length): IP头部的⻓…...
源码编译安装LNMP
LNMP包含: linux、Nginx、Mysql、php LNMP的工作原理 : 由客户端发送页面请求给Nginx,Nginx会根据location匹配用户访问请求的URL路径判断是静态还是动态,静态的一般是以 .html .htm .css .shtml结尾,动态的一般是以 .…...
安装Chrome扩展程序来 一键禁用页面上的所有动画和过渡。有那些扩展程序推荐一下
要安装Chrome扩展程序来一键禁用页面上的所有动画和过渡,以下是一些推荐的扩展程序: Toggle CSS Animations and Transitions 功能:此扩展程序允许用户轻松地在网页上切换CSS动画和过渡的开启与关闭状态。使用方法:安装后&#x…...
读人工智能时代与人类未来笔记19_读后总结与感想兼导读
1. 基本信息 人工智能时代与人类未来 (美)亨利基辛格,(美)埃里克施密特,(美)丹尼尔胡滕洛赫尔 著 中信出版社,2023年6月出版 1.1. 读薄率 书籍总字数145千字,笔记总字数39934字。 读薄率39934145000≈27.5% 1.2. 读厚方向 千脑智能 脑机穿越 未来呼啸而来 …...
个人影响力
华人出了个黄仁勋,世界级影响力,还是近代华人历史首次出现具有如此影响力的人。凭借的逻辑是什呢?在人工智能领域有巨大影响力。...
OBS实现多路并发推流
OBS实现多路并发推流 解决方案速览相关依赖下载安装多路推流 解决方案速览 利用OBS进行本地直播画面的构建。 使用Multiple RTMP outputs plugin进行多路并发推流。 相关依赖下载安装 OBS软件 # OBS官网 https://obsproject.com/zh-cnMultiple RTMP outputs plugin # 插件官网…...
JDK环境配置、安装
DK环境配置(备注:分32位与64位JDK,32位电脑只能按照32位JDK,64位电脑兼容32、64位JDK) 一、检查自己电脑是否安装过JDK 1.在电脑屏幕左下角,输入命令提示符CMD,打开命令提示符应用 2.在打开界…...
莱富康压缩机的选型软件介绍
下载地址 https://download.csdn.net/download/jintaihu/16295771 安装步骤 这里可以选制冷系统的参数,最后在压缩机列表内选择推荐的型号。...
Pr 2024下载安装,Adobe Premiere专业视频编辑软件安装包获取!
Premiere Pro,简称PR,无论是想要剪辑家庭录像,还是制作专业的影视作品,Premiere Pro都能为您提供强大的支持。 Premiere Pro以其卓越的编辑功能和强大的性能,助力用户在视频创作的道路上不断突破自我。 它具备丰富的视…...
MySQL事务与MVCC
文章目录 事务和事务的隔离级别1.为什么需要事务2.事务特性1_原子性(atomicity)2_一致性(consistency)3_持久性(durability)4_隔离性(isolation) 3.事务并发引发的问题1_脏读2_不可重…...
【数据结构】链式二叉树详解
个人主页~ 链式二叉树基本内容~ 链式二叉树详解 1、通过前序遍历的数组来构建二叉树2、二叉树的销毁3、二叉树节点个数4、二叉树叶子节点个数5、二叉树第k层节点个数6、二叉树查找7、前序遍历8、中序遍历9、后序遍历10、层序遍历与检查二叉树是否为完全二叉树Queue.hQueue.c层序…...
PHP面向对象编程总结
PHP面向对象编程总结 学习PHP时,面向对象编程(OOP)往往是一个重要的里程碑。PHP的OOP功能提供了一种更加模块化、可扩展和易于维护的代码结构。在本文中,我们将深入探讨PHP面向对象编程的各个方面,包括类与对象、访问控…...
linux中的“->“符号
问: "->“符号在Linux中是什么意思。 例如:当我在一个特定的文件夹中执行ls -l时,我得到了以下结果。 lrwxrwxrwx 1 root root 11 May 16 13:30 nexus3 -> /nexus-data lrwxrwxrwx 1 root root 29 Feb 27 12:23 ojdbc.jar -&g…...
MySql 数据类型选择与优化
选择优化的数据类型 更小的通常更好 一般情况下尽量使用可以正确存储数据的最小类型。更小的数据类型通常更快,因为它们占用更少的磁盘,内存和CPU缓存,并且处理时需要的CPU周期也更少。但也要确保没有低估需要存储值的范围。 简单就好 简单的…...
HTML静态网页成品作业(HTML+CSS)——家乡常德介绍网页(1个页面)
🎉不定期分享源码,关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 🏷️本套采用HTMLCSS,未使用Javacsript代码,共有1个页面。 二、作品演示 三、代…...
【ARMv7-A】——CP15 协处理器
文章目录 CP15 协处理器指令格式MCR 示例MRC 示例寄存器C0 identification registersC1 system control registersC2 memory protection and control registersC3 memory protection and control registersC4 Not usedC5 Memory system fault registers...
学习笔记:(2)荔枝派Nano开机显示log(全志F1C200S)
学习笔记:TF卡启动荔枝派Nano(全志F1C200S) 1.u-boot配置2.需要配置LCD的显示设备树1.u-boot配置 ARM architecture Enable graphical uboot console on HDMI, LCD or VGAx:480,y:272,depth:...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果是一个支持网页浏览器进行实时语音…...
ubuntu22.04 安装docker 和docker-compose
首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...
