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

C++ lambda表达式

C++ lambda表达式

功能

C++11引入了lambda表达式,lambda表达式支持你定义一个局部的匿名函数,通常这个函数是一次性使用的

格式

[capture-list](parameter)mutable->return-type{statement}
  • parameter:参数列表,对应有名函数的形参,不需要参数可以省略
  • return-type:返回值,可以省略,省略后返回值类型由编译器自动推导,若不省略返回值则参数列表也不能省略
  • statement:函数体,不能省略
  • capture-list:捕捉列表,不能省略

注意:lambda表达式是有类型的,类型大多数情况是随机的,调用lambda表达式需要使用一个变量接收lambda表达式,通常与auto配合使用

最简单的lambda表达式

int main()
{auto i = [] {std::cout << "lambda" << std::endl; };i();//调用lambda表达式//运行结果://lambdareturn 0;
}
  • 除了格式以外,lambda表达式和有名函数都差不多,都有参数返回值函数体,唯一不同的是捕捉列表和mutable

lambda表达式实现swap

int main()
{auto Swap = [](int& x, int& y){int temp = x;x = y;y = temp;};int x = 10, y = 20;Swap(x, y);std::cout << "x = " << x << " , y = " << y << std::endl;//运行结果://x = 20 , y = 10return 0;
}

捕捉列表和mutable

捕捉列表:指定lambda函数体内可以访问的外部变量。

  • 如果是值捕捉,那么捕捉的是外部变量的拷贝,修改拷贝不会影响外部变量
  • 如果是引用捕捉,那么捕捉的就是外部变量本身,修改捕捉变量就是修改外部变量
  • 值捕捉时,捕捉到的变量默认是无法被修改的,如果需要修改则需要加上mutable
  • 引用捕捉时,捕捉到的变量默认可以被修改,加不加mutable均可
  • 如果需要加上mutable,则必须要带上参数列表,哪怕参数列表为空

捕捉方式:

  • [a,b] 传值捕捉
  • [&a,&b] 传引用捕捉
  • [=] 传值捕捉方式父作用域中所有变量(包括this指针)
  • [&] 传引用捕捉方式父作用域中所有变量(包括this指针)
  • 混合使用,例如捕捉[&x,y]

值捕捉:

int main()
{int x = 10, y = 20;//值捕捉,捕捉的时x和y的拷贝,默认x和y无法被修改,加上mutable则可以修改x和yauto print = [x, y]()mutable{x = 1;y = 2;std::cout << "x = " << x << " , y = " << y << std::endl;};print();//运行结果://x = 1, y = 2return 0;
}

引用捕捉:

int main()
{int x = 10, y = 20;//引用捕捉,捕捉的是外部变量本身,默认可以被修改auto Swap = [&x, &y] (){int temp = x;x = y;y = temp;};Swap();std::cout << "x = " << x << " , y = " << y << std::endl;//运行结果://x = 20 , y = 10return 0;
}

捕捉列表的其他捕捉方式

  • [=]表示的是以值捕捉的方式捕捉lambda的父作用域的所有变量
  • [&]表示的是以引用捕捉的方式捕捉lambda的父作用域的所有变量
int main()
{int x = 10, y = 20;//值捕捉方式捕捉main函数中的所有变量auto print1 = [=]()mutable{x = 1;y = 2;std::cout << "x = " << x << " , y = " << y << std::endl;};//引用捕捉方式捕捉main函数中的所有变量auto print2 = [&](){x = 1;y = 2;std::cout << "x = " << x << " , y = " << y << std::endl;};print1();print2();//运行结果://x = 1, y = 2//x = 1, y = 2return 0;
}
  • 混合捕捉:lambda表达式支持一部分外部变量传值捕捉,一部分外部变量引用捕捉

特例:

  • [=, &a, &b]:使用值传递方式捕获所有变量,除了 a 和 b,这两个变量将以引用方式捕获。
  • [&,a, this]:使用引用传递方式捕获变量 a 和 this,其他变量将以值传递方式捕获。
  • [=, a]:这个捕捉列表是错误的,因为 = 已经以值传递方式捕获了所有变量,而 a 也被重复捕获了。
  • 在块作用域以外的lambda函数:捕捉列表必须为空。这意味着lambda不能捕获任何外部变量。
  • 在块作用域中的lambda函数:只能捕获父作用域中局部变量。
int main(){int a = 0;int b = 1;int c = 2;int d = 3;const int e = 1;cout << &e << endl;// 引用的方式捕捉所有对象,除了a// a用传值的方式捕捉auto func = [&, a] {//a++;b++;c++;d++;//e++;cout << &e << endl;//const & 得到的是同一个地址};func();return 0;
}

lambda表达式的原理

仿函数:

#include <iostream>class Add {
public:int operator()(int a, int b) const {return a + b;}
};int main() {Add add;int result = add(10, 20);std::cout << "Result: " << result << std::endl;return 0;
}
  • 仿函数汇编底层
    在这里插入图片描述

lambda底层使用的是用仿函数实现的

auto print = [] {
008123AF  xor         eax,eax  
008123B1  mov         byte ptr [ebp-0D5h],al  std::cout << "Hello , World" << std::endl;};print();
008123B7  lea         ecx,[print]  
008123BA  call        <lambda_0596717148ff8a7e4509ccb4cd363cfe>::operator() (0812190h) 
  • 汇编层面,调用lambda最关键的一步是call一个类型的operator(),实际上跟仿函数是一摸一样的,这个仿函数类型是lambda_+字符串,字符串是由系统的算法随机生成的,这个随机生成的字符串重复概率很低

lambda表达式的赋值

lambda表达式之间不能相互赋值

  • 每一个lambda表达式(哪怕函数体一模一样),调用时底层的仿函数类都不相同,所以无法让两个lambda表达式之间相互赋值
  • 如果需要再获得一个lambda表达式的变量,可以以原来接收的变量为模板,auto生成一个相同类型的变量
  • 如果某个容器/函数中需要传一个仿函数的类型,那么你可以使用decltype,lambda变量生成的仿函数类的默认构造是被禁掉的,但拷贝构造没有被禁掉
int main()
{//1、print1和print2无法相互赋值auto print1 = [] {std::cout << "Hello , World" << std::endl;};auto print2 = [] {std::cout << "Hello , World" << std::endl;};//2、以auto生成一个print1变量的副本auto print1_temp = print1;//3、以decltype把comp_less的类型传给func//lambda变量的默认构造被delete了,需要使用拷贝构造传参auto comp_less = [](int* x, int* y) {return *x < *y;};std::priority_queue<int*, std::vector<int*>, decltype(comp_less)> pq(comp_less);return 0;
}
http://www.xdnf.cn/news/489061.html

相关文章:

  • 构建稳定的金字塔模式生态:从自然法则到系统工程
  • LVGL常见面试题
  • 腾讯云MCP数据智能处理:简化数据探索与分析的全流程指南
  • S32DS中定义的全局变量对应的路径查看${ProjDirPath}
  • ConcurrentSkipListMap的深入学习
  • 中国 MRO 的市场概况及发展趋势
  • LlamaIndex 第九篇 Indexing索引
  • C# RSA加密
  • No module named‘serial‘解决办法
  • 计算机视觉----感兴趣区域(ROI)、非极大值抑制
  • 日语简单记录
  • 物联网设备远程管理:基于代理IP的安全固件更新通道方案
  • 共有四个站进行码分多址CDMA通信。四个站的码片序列为......
  • 地磁传感器RM3100简单介绍
  • Socket echo server
  • APIfox参数化配置
  • 移动硬盘不显示容量?三步找回你的重要数据
  • BUFDS_GTE2,IBUFDS,BUFG缓冲的区别
  • 深度学习反向传播:从数学推导到实战解析
  • `ParameterizedType` 和 `TypeVariable` 的区别
  • C#实现访问远程硬盘(附源码)
  • 代码随想录 算法训练 Day3:链表1
  • X_T 转 STP 技术全解:从格式剖析到软件实操与迪威模型网在线转换指南
  • 数据标注的黄金标准:如何为机器学习构建可靠标签?
  • **练习案例2:点和圆的关系**设计一个圆形类(Circle),和一个点类(Point),计算点和圆的关系。
  • ML307R 的 USB Vendor ID (VID):0x2ECC ML307R 的 USB Product ID (PID):0x3012
  • 驱动芯片走线、过孔指导,大电流、散热过孔
  • Emacs 折腾日记(二十四)——帮助信息的一些优化
  • 【python实用小脚本-64】Python如何用图像比对解决办公效率难题?(附方案)
  • H3C华三配置AC自动上线AP