7.3 lambda函数

一、语法

1.基础语法

[capture](paramLists) mutable ->retunType{statement}
  • capture。捕获列表,用于捕获前文的变量供lambda函数中使用,可省略。
  • (paramLists)。参数列表,可省略。
  • mutable。lambda表达式默认具有常量性,可以通过mutable取消常量性,可省略。
  • returnType。函数返回类型,可省略。
  • statement。函数体,可省略。

结合上述可省略的内容,C++11中最简单的lambda表达式可以是(虽然没有实际意义):

[]{}

2.捕获列表

lambda函数的与普通函数最大的区别在于可以捕获前文的局部变量(仅仅对于局部而言,如果是全局lambda函数则不支持)。而捕获的方式有:

  • [var]表示值传递方式捕获变量var
  • [=]表示值传递方式捕获父作用域所有变量(包括this)
  • [&var]表示引用传递方式捕获变量var
  • [&]表示引用传递方式捕获父作用域所有变量(包括this)
  • [this]表示值传递方式捕获变量this

而由于捕获列表支持多个值(用,分隔),因此可以进行组合:

  • [=,&a,&b]表示引用传递捕获a,b,值传递捕获其他内容。
  • [&,a,this]表示值传递捕获a,this,引用传递捕获其他内容。

需要注意的是,捕获列表不能重复,如

[=,a,b]或者[&,&a,&b]等都是重复捕获(以相同的传递方式捕获同一个变量)。

3.基础使用

lambda函数通常用于局部作用域作为局部[匿名]函数。

        extern int z;extern float c;void Calc(int& , int, float &, float);void TestCalc() {int x, y = 3;float a, b = 4.0;int success = 0;auto validate = [&]() -> bool{if ((x == y + z) && (a == b + c))return 1;elsereturn 0;};Calc(x, y, a, b);success += validate();y = 1024;b = 1e13;Calc(x, y, a, b);success += validate();}// 编译选项:g++ -c -std=c++11 7-3-7.cpp

而在有时会通过auto为lambda函数命名,使其获得自说明性。

与普通函数相比lambda有如下优势:

  • 支持直接在函数内创建,作用域外释放,而不用额外创建一个函数。
  • 能够直接捕获所有局部变量,而普通函数则需要额外传递。
  • lambda函数默认内联,在较多次调用时性能比普通函数好。
  • lambda函数的设计更简单,不需要考虑参数传递等问题

二、关于lambda的一些实验与讨论

1.捕获参数的传递方式

lambda函数中不同的捕获传递方式会造成不同的结果,对于值传递,则在传递的值在编译期就确定了,无法被修改,而对于引用传递则可以同步lambda函数外的修改。

        #include <iostream>using namespace std;int main() {int j = 12;auto by_val_lambda = [=] { return j + 1;};auto by_ref_lambda = [&] { return j + 1;};cout << "by_val_lambda: " << by_val_lambda() << endl;cout << "by_ref_lambda: " << by_ref_lambda() << endl;j++;cout << "by_val_lambda: " << by_val_lambda() << endl;cout << "by_ref_lambda: " << by_ref_lambda() << endl;}

运行结果:

        by_val_lambda: 13by_ref_lambda: 13by_val_lambda: 13by_ref_lambda: 14

2.与函数指针的关系

lambda函数与函数指针看起来很相似,但是实际上却不是函数指针,它是一种称为"闭包"(closure)的类。

这种类型支持向函数指针转换,前提是:

  • lambda函数不捕获任何变量
  • 函数指针的原型与lambda一致(参数,返回值都完全一致)
        int main() {int girls = 3, boys = 4;auto totalChild = [](int x, int y)->int{ return x + y; };typedef int (*allChild)(int x, int y);typedef int (*oneChild)(int x);allChild p;p = totalChild;oneChild q;q = totalChild;      // 编译失败,参数必须一致decltype(totalChild) allPeople = totalChild;   // 需通过decltype获得lambda的类型decltype(totalChild) totalPeople = p;       // 编译失败,指针无法转换为lambdareturn 0;}// 编译选项:g++ -std=c++11 7-3-10.cpp

此外,不支持函数指针向lambda转换

3.常量性与mutable

前面提到对于值传递的捕获参数具有常量性无法被修改,而想要打破这一限制,可以加上mutable关键字。(注意虽然可以修改,但仍然不影响父作用域变量)

#include <iostream>
int main() {int val=0;// 编译失败, 在const的lambda中修改常量//auto const_val_lambda = [=]() { val = 3; };// 非const的lambda,可以修改常量数据auto mutable_val_lambda = [=]() mutable { val = 3; };mutable_val_lambda();std::cout << val << std::endl;// 依然是const的lambda,不过没有改动引用本身auto const_ref_lambda = [&] { val = 4; };const_ref_lambda();std::cout << val << std::endl;// 依然是const的lambda,通过参数传递valauto const_param_lambda = [&](int v) { v = 5; };const_param_lambda(val);std::cout << val << std::endl;return 0;
}

而对于引用传递方式,则表示lambda捕获的参数引用了父作用域的变量,一边修改都会同步到另一边。

三、lambda与STL

前面说到,lambda对C++11最大的贡献,或者说是改变,应该在STL库中。这主要体现于STL算法更加容易,也更加容易学习了(可读性更高)。

下面将以for_each为例,讲述lambda带来的便捷。

#include <vector>
#include <algorithm>
using namespace std;
vector<int> nums;
vector<int> largeNums;
const int ubound = 10;
inline void LargeNumsFunc(int i){if (i > ubound)largeNums.push_back(i);
}
void Above() {// 传统的for循环for (auto itr = nums.begin(); itr != nums.end(); ++itr) {if (*itr >= ubound)largeNums.push_back(*itr);}// 使用函数指针for_each(nums.begin(), nums.end(), LargeNumsFunc);// 使用lambda函数和算法for_eachfor_each(nums.begin(), nums.end(), [=](int i){if (i > ubound)largeNums.push_back(i);});
}
编译选项: g++ 7-3-13.cpp -c -std=c++11

这是通过基础for循环、for_each和lambda实现查找大于某个值的功能。相比for循环而言,for_each只需要关心数据起始点,并将每个元素作用到指定的操作上即可,在效率、正确性、可维护性上都具有一定优势。

而lambda较for_each而言,首先其函数内容会直接放在调用处,可阅读性更高(当然,有时也会被分离出来并命名,但通常不会太远);其次使用函数指针很可能导致编译器不对其进行inline优化(inline对编译器而言并非强制),在循环次数较多的时候,内联的lambda和没有能够内联的函数指针可能存在着巨大的性能差别。

此外相较于仿函数(不论是自己实现还是内置仿函数),lambda也依旧存在着不小的优势。

#include <vector>
#include <algorithm>
using namespace std;
vector<int> nums;
vector<int> largeNums;
class LNums{
public:
LNums(int u): ubound(u){}
void operator () (int i) const
{if (i > ubound)largeNums.push_back(i);
}
private:
int ubound;
};
void Above(int ubound) {// 传统的for循环for (auto itr = nums.begin(); itr != nums.end(); ++itr) {if (*itr >= ubound)largeNums.push_back(*itr);}// 使用仿函数for_each(nums.begin(), nums.end(), LNums(ubound));// 使用lambda函数和算法for_eachfor_each(nums.begin(), nums.end(), [=](int i){if (i > ubound)largeNums.push_back(i);});
}

对于自己实现的仿函数,很直观的,lambda更加简洁。

而当面对更加复杂的场景时,lambda显得更加有优势:

#include <vector>
#include <algorithm>
using namespace std;
extern vector<int> nums;
void TwoCond(int low, int high) {// 传统的for循环for (auto i = nums.begin(); i != nums.end(); i++)if (*i >= low && *i < high) break;// 利用了3个内置的仿函数,以及非标准的compose2find_if(nums.begin(), nums.end(),compose2(logical_and<bool>(),bind2nd(less<int>(), high),bind2nd(greater_equal<int>(), low)));// 使用lambda函数find_if(nums.begin(), nums.end(), [=](int i) {return i >= low && i < high;});
}

这里我们需找到vector nums中第一个值介于[low, high)间的元素,可以看到内置仿函数变得异常复杂。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电阻的运用

本文引注 https://baijiahao.baidu.com/s?id1749115196647029942&wfrspider&forpc 一、零欧电阻 在电子电路设计时经常用到的一种元件就是电阻&#xff0c;我们都知道电阻在电路中起到分压限流的作用。然而&#xff0c;实际使用时会用到一种特殊的电阻&#xff1a;零…

用Kotlin抓取微博数据并进行热度预测

闲来无事&#xff0c;逛逛微博&#xff0c;看着每条热度很高的博文趣事&#xff0c;心想能否通过爬虫抓取微博热度并进行趋势分析&#xff0c;说干就干&#xff0c;这里需要注意的问题我会一一标注。 爬虫ip信息的设置是在爬虫程序中进行的。爬虫ip信息可以帮助爬虫程序在访问…

使用React实现随机颜色选择器,JS如何生成随机颜色

背景 在标签功能中&#xff0c;由于有「背景色」属性&#xff0c;每次新增标签时都为选择哪种颜色犯难。因此&#xff0c;我们思考如何通过JS代码生成随机颜色&#xff0c;提取一个通用的随机颜色生成工具&#xff0c;并基于React框架封装随机颜色选择器组件。 实际效果 原理…

RabbitMQ 命令

Docker # 进入容器 > docker exec -it rabbitmq /bin/bash# 帮助 > rabbitmq-service help# 查看所有队列 > rabbitmqctl list_queues Windows 进入安装目录【D:\Program Files\RabbitMQ Server\rabbitmq_server-3.9.10\sbin】输入cmd # 帮助 > rabbitmq-servic…

在Node.js中停止使用dotenv

dotenv 是一个从 .env 文件中加载环境变量的包&#xff0c;npm 周下载量有三千五百万。在 Node.js v20.6.0 中&#xff0c;已经内置了对 .env 文件的支持&#xff0c;这篇文章将演示如何在 Node.js 中访问 .env 中的环境变量。 配置 创建一个最简单的 Express 应用程序&#…

融云即时通讯的产品优势

1、开箱即用的 UI 组件 让开发者不用撰写 UI 界面代码即可快速实现 IM 功能 2、健壮稳定 全球范围提供多个数据中心、物理覆盖全球用户 3、快速接入 标准通信功能 1 天即可快速接入 4、接口丰富 满足复杂业务需求 详情查看&#xff1a;融云-为用户提供IM即时通讯和实时音视频通…

Parade Series - Message Interaction

if (true) {Swal.fire("节目发布", "发布完毕", "success");event.preventDefault(); } if (false) {Swal.fire("节目发布", "发布失败", "error");event.preventDefault(); }if (true) {for (var i 0; i < b…

AI日报:OpenAI向新用户重新开放ChatGPT Plus订阅

欢迎订阅专栏 《AI日报》 获取人工智能邻域最新资讯 文章目录 总览Chatgptplus重新开放订阅#暂停原因功能 OpenAI的1000万美元安全人工智能拨款拨款初衷学术捐赠 总览 ChatGPT Plus再次向新用户开放&#xff0c;但目前每三小时限制发送40条消息。 OpenAI还宣布拨款1000万美元…

点云从入门到精通技术详解100篇-基于单双目结合的结构光三维重建

目录 前言 国内外研究现状 三维测量技术研究现状 结构光研究现状

微信小程序 - 龙骨图集拆分

微信小程序 - 龙骨图集拆分 注意目录结构PC端延时动画废话一下业务逻辑注意点龙骨JSON图集结构源码分享dragonbones-split.jsdragonbones-split.jsondragonbones-split.wxmldragonbones-split.wxssimgUtil.js参考资料注意 只支持了JSON

数据结构-07-二叉树

前面学习的栈、队列等等都是线性表结构。树是一种非线性表结构&#xff0c;比线性表的数据结构要复杂。 1-树tree “树”这种数据结构类似我们现实生活中的“树”&#xff0c;这里面每个元素我们叫作“节点”&#xff1b;用来连线相邻节点之间的关系&#xff0c;我们叫作“父子…

Kafka Avro序列化之三:使用Schema Register实现

为什么需要Schema Register 注册表 无论是使用传统的Avro API自定义序列化类和反序列化类 还是 使用Twitter的Bijection类库实现Avro的序列化与反序列化,这两种方法都有一个缺点:在每条Kafka记录里都嵌入了schema,这会让记录的大小成倍地增加。但是不管怎样,在读取记录时…

云端赋能大湾区:华为云照亮数字化转型之路

编辑&#xff1a;阿冒 设计&#xff1a;沐由 在中国的经济版图上&#xff0c;大湾区是极其重要的增长引擎。这块富有活力和创新力的经济区域里&#xff0c;荟聚了大量的高新技术企业&#xff0c;以及一批创新孵化器和科研机构&#xff0c;产业升级和技术创新的氛围格外浓烈。 1…

山峰个数 - 华为OD统一考试

OD统一考试 分值: 100分 题解: Java / Python / C++ 题目描述 给定一个数组,数组中的每个元素代表该位置的海拔高度。0表示平地,>=1时表示属于某个山峰,山峰的定义为当某个位置的左右海拔均小于自己的海拔时,该位置为山峰。数组起始位置计算时可只满足一边的条件。 …

lv12 linux内核的安装与加载

目录 1 tftp加载Linux内核及rootfs 1.1 uboot内核启动命令 1.2 uboot自启动参数环境变量 1.3 实验 2 EMMC加载Linux 内核及rootfs ​编辑 2.1 emmc中写入uimage ​编辑 2.2 emmc中写入dtb 2.3 emmc中写入根文件系统 2.4 设置环境变量 3 tftp加载Linux内核nfs挂载ro…

centos 手动编译安装git

原因 由于centos自带的git版本太低&#xff0c;使用git的时候会出现很多问题&#xff0c;但是尝试了各种办法无法直接更新git版本&#xff0c;所以最后自己手动编译安装git 在github下载源码&#xff0c;下载解压之后&#xff0c;上传到服务器&#xff0c;我上传到 /home/user…

用python做餐饮业的数据分析

问题: 1&#xff0c;订单表的长度 shape columns 2&#xff0c;统计菜单的平均价格&#xff08;amount&#xff09; 3&#xff0c;什么菜最受欢迎 订单 客户 时间 菜品 这几个维度 4&#xff0c;哪个订单ID点的菜最多等问题 首先在jupyter note里导入模块 import numpy …

探索多功能SQL数据库编辑器 - Richardson Software RazorSQL

在当今数字化时代&#xff0c;SQL数据库的管理和编辑是许多企业和开发人员必不可少的任务。为了提高生产力和简化数据库操作&#xff0c;Richardson Software推出了一款强大而多功能的SQL数据库编辑器 - RazorSQL。 RazorSQL是一款功能全面的数据库管理工具&#xff0c;可适用…

LeetCode(61)删除链表的倒数第 N 个结点【链表】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 删除链表的倒数第 N 个结点 1.题目 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例…

Lua 元表,元方法

元表与元方法的概念 Lua中每个值都可具有元表。元表是普通的Lua表,定义了原始值在某些特定操作下 的行为。 例如,当table作为加法的操作数时,Lua检查其“元表”中的“__add”字段是否有 个函数。如果有,Lua调用它执行加法。我们称“元表”中的“键(如__add)”为事件(event),称…