C++ Lamda表达式
- Lamda表达式作用:
Lambda表达式类似Javascript中的闭包,它可以用于创建并定义匿名的函数对象,以简化编程工作。 - Lambda的语法如下:
[函数对象参数](操作符重载函数参数)->返回值类型{函数体}
vector<int> iv{5, 4, 3, 2, 1};
int a = 2, b = 1;
int foo = 3;for_each(iv.begin(), iv.end(), [b](int &x){cout<<(x + b)<<endl;}); // (1) for_each(iv.begin(), iv.end(), [=](int &x){x *= (a + b);}); // (2) for_each(iv.begin(), iv.end(), [=](int &x)->int{return x * (a + b);});// (3)
• []内的参数指的是Lambda表达式可以取得的全局变量。
(1)函数中的b就是指函数可以得到在Lambda表达式外的全局变量,如果在[]中传入=的话,即是可以取得所有的外部变量,如(2)和(3) Lambda表达式
• ()内的参数是每次调用函数时传入的参数。
• ->后加上的是Lambda表达式返回值的类型,如(3)中返回了一个int类型的变量
• 下面是各种变量捕获的选项:
o [] 不捕获任何变量
o [&} 捕获外部作用域中所有变量,并作为引用在函数体中使用
o [=] 捕获外部作用域中所有变量,并拷贝一份在函数体中使用
o [=, &b] 捕获外部作用域中所有变量,并拷贝一份在函数体中使用,但是对b变量使用引用
o [b] 捕获b变量并且拷贝一份在函数体使用,同时不捕获其他变量
o [this] 捕获当前类中的this指针。如果已经使用了&或者=就默认添加此选项。
小结: Lamda 表达式主要用于创建匿名的函数对象,简化编码。最重要的就是[函数对象参数] 的使用。对于上面各种变量捕获的选项,需要熟记。
下面给出一些样例:
- [] 不捕获任何变量
std::vector<int> tempVec{1, 2, 3, 4, 5};
int a = 8;
int b = 2;
for_each(tempVec.begin(), tempVec.end(), [](int& x){cout << x + b << endl;
});
报错:
error: 'b' is not capturedcout << x + b << endl;^
F:\clion project\main.cpp:67:47: note: the lambda has no capture-defaultfor_each(tempVec.begin(), tempVec.end(), [](int& x){
捕获列表为空,默认不获取任何变量,所以Lamda表达式内部使用 b 会报 未捕获。
- [&} 捕获外部作用域中所有变量,并作为引用在函数体中使用
std::vector<int> tempVec{1, 2};
int a = 8;
int b = 2;
cout << "a addr:" << &a << endl;
cout << "b addr:" << &b << endl;
for_each(tempVec.begin(), tempVec.end(), [&](int& x){cout << x + b + a<< endl;cout << "capture a addr: " << &a << endl;cout << "capture b addr: " << &b << endl;
});
输出结果:
a addr:0x61fd8c
b addr:0x61fd88
11
capture a addr: 0x61fd8c
capture b addr: 0x61fd88
12
capture a addr: 0x61fd8c
capture b addr: 0x61fd88
可以看出a, b 的地址在 Lamda表示的外部和内部是相同的,即a, b是作为引用传递到Lamda表达式内部的。
- [=] 捕获外部作用域中所有变量,并拷贝一份在函数体中使用
std::vector<int> tempVec{1, 2};
int a = 8;
int b = 2;
cout << "a addr:" << &a << endl;
cout << "b addr:" << &b << endl;
for_each(tempVec.begin(), tempVec.end(), [=](int& x){cout << x + b + a<< endl;cout << "capture a addr: " << &a << endl;cout << "capture b addr: " << &b << endl;
});
输出:
a addr:0x61fdac
b addr:0x61fda8
11
capture a addr: 0x61fd84
capture b addr: 0x61fd80
12
capture a addr: 0x61fd84
capture b addr: 0x61fd80
可以看出Lamda 表达式的内部和外部 使用的是不同地址的a, b变量,说明是拷贝了外部变量传递到表达式内部的。有一个问题: 为什么 内部 连续2次输出的a, b 变量是同一个地址呢? 这个留到后面讲解Lamda表达式的具体实现。可以简单理解为a,b 被复制了一份存储到函数对象内部,作为成员变量。
- [=, &b] 捕获外部作用域中所有变量,并拷贝一份在函数体中使用,但是对b变量使用引用
std::vector<int> tempVec{1, 2};
int a = 8;
int b = 2;
cout << "a addr:" << &a << endl;
cout << "b addr:" << &b << endl;
for_each(tempVec.begin(), tempVec.end(), [=, &b](int& x){cout << x + b + a<< endl;cout << "capture b addr: " << &b << endl;cout << "capture a addr: " << &a << endl;
});
输出:
a addr:0x61fd8c
b addr:0x61fd88
11
capture b addr: 0x61fd88
capture a addr: 0x61fd20
12
capture b addr: 0x61fd88
capture a addr: 0x61fd20
可以看出b的地址在 Lamda表示的外部和内部是相同的,但是a 的是不同的,说明a 是拷贝了一份,b 是传递的引用。
- [b] 捕获b变量并且拷贝一份在函数体使用,同时不捕获其他变量
void Test() {int a = 8;int b = 10;auto func = [b]{cout << a << endl;cout << b << endl;};func();
}
输出:
In lambda function:
F:\clion project\main.cpp:83:17: error: 'a' is not capturedcout << a << endl;^
编译报错,因为捕获列表只有b, 没有a。 只捕获显示说明需要捕获的变量。
删除对a的引用后的代码和输出:
void Test() {int a = 8;int b = 10;cout << &b << endl;auto func = [b]{cout << b << endl;cout << &b << endl;};func();
}
输出:
0x61fde4
10
0x61fde0
可以看到Lamda表达式内部和外部的b 地址不同,说明b 被拷贝了一份到 内部。
- [this] 捕获当前类中的this指针。如果已经使用了&或者=就默认添加此选项
class Test
{public:void hello() {cout << "test hello" << endl;};void lambda() {auto func = [&]{ // 捕获了 this 指针this->hello(); // 这里 this 调用的就是 class test 的对象了cout << "private param of object:" << this->a << endl;};func();}private:int a{10};
};
输出:
test hello
private param of object:10
捕获列表是&, 默认捕获了this 指针;捕获列表是 = , 输出结果跟上面相同。说明this 指针也被默认捕获。
再说明几个特殊的场景:
- 捕获变量定义在表达式之后
int main()
{auto func = [=]{cout << a << endl;};func();int a = 10;return 0;
}
输出:
F:\clion project\main.cpp:82:17: error: 'a' was not declared in this scopecout << a << endl;^
通过上面的例子,说明Lamda表达式只能捕获在自身定义之前的变量。
- 捕获的变量在表达式之后有操作
int main()
{int a = 10;int b = 5;auto func = [=, &b]{cout << "a is:" << a << endl;cout << "b is:" << b << endl;};a = 20;b = 8;func();return 0;
}
输出:
a is:10
b is:8
b作为引用,变化影响了表达式的输出;但是a 作为拷贝,输出的仍然是表达式定义前的值。这个原因是什么?下面的部分会讲解。
- 捕获静态变量
int main()
{static int c = 6;auto func = [c]{cout << "c is:" << c << endl;};c = 8;func();return 0;
}
输出:
warning: capture of variable 'c' with non-automatic storage durationauto func = [c]{^
c is:8
静态变量出现在捕获列表中会出现告警,同时 它也不符合 捕获列表的行为(c会拷贝一份到内部,同时不受外部影响),c 在外部被操作,仍然影响了表达式的输出。
-3. Lambda表达式的内部实现
当编写一个lambda表达式之后,编译器将该表达式翻译成一个未命名类的未命名对象。
所以上面说的拷贝 实际是在这个表达式定义的未命名类中添加了成员变量,并在表达式翻译成对象时完成初始化;所以地址和 值 在 表达式定义的地方确定,外部的其他操作不会再影响它,同时它的地址在循环中调用时总是不变。
对于引用捕获,编译器则直接使用该变量,所以需要程序自身保证引用时外部变量是有效的。
捕获列表只用于局部非static变量,lambda表达式可以直接使用局部static变量和它所在函数之外声明的名字;所以上面的例子中捕获了静态变量会告警,同时外部的修改也会影响表达式的输出。
参考博客: https://www.jianshu.com/p/81bc3fd67d4a 写的很好,推荐阅读。
C++ Lamda表达式相关推荐
- 委托、lamda表达式..委托概念-匿名函数-泛型委托-Lamda表达式-多播委托
委托 一.什么是委托? 将一个方法作为参数传递给另一个方法(参数类型为委托delegate). 声明一个委托类型. 委托所指向的函数必须跟委托具有相同的的签名(参数个数.参数类型.返回值一样). ...
- 为了去重复,写了一个通用的比较容器类,可以用在需要比较的地方,且支持Lamda表达式...
为了去重复,写了一个通用的比较容器类,可以用在需要比较的地方,且支持Lamda表达式,代码如下: public class DataComparer<T>:IEqualityCompare ...
- Java --Lamda表达式
Lamda:属于函数式编程的概念: interface IMessage {public void print() ; } public class TestDemo {public static v ...
- 什么是lamda表达式?
我们首先来看一下这一片代码: 1 class Solution { 2 public: 3 int singleNumber(int A[], int n) { 4 // Note: The Solu ...
- [.net 面向对象程序设计进阶] (7) Lamda表达式(三) 表达式树高级应用
[.net 面向对象程序设计进阶] (7) Lamda表达式(三) 表达式树高级应用 本节导读:讨论了表达式树的定义和解析之后,我们知道了表达式树就是并非可执行代码,而是将表达式对象化后的数据结构.是 ...
- 带参的信号、lamda表达式及坐标系统
带参的信号.lamda表达式及坐标系统 文章目录 带参的信号.lamda表达式及坐标系统 一.带参的信号 二.lamda表达式 三.坐标系统 一.带参的信号 1. 自定义信号(这个信号不像标准信号是一 ...
- 静态内部类、局部内部类、匿名内部类、lamda表达式
普通的继承: 静态内部类( 必须有static修饰): 局部内部内(写在任意方法中的): 匿名内部类(可以写在任意位置): lamda表达式(感觉像匿名内部类的简化)(前题是函数式接口:接口中只有一个 ...
- 一个小案例精通lamda表达式与函数式接口
前言:只有基础(多态.匿名接口实现类)足够扎实才能更好的学习花拳绣腿(lamda表达式),Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性.Lambda 允许把函数作为一 ...
- c++ lamda表达式调用自身实现定时器
刚好要做一个定时器,用asio的deadline_timer,通过异步等待方式去做,超时后重新设置超时时间再重新异步等待. 将lamda表达式放到function中,异步等待的时候再调用自身. 具体代 ...
- [ASP.NET MVC]让Html.RenderAction支持Lamda表达式
今天在ASP.NET MVC代码时用到了Html.RenderAction,代码如下: @{Html.RenderAction("RecentNews")} 通过字符串指定Acti ...
最新文章
- Python如何实现单步调试
- 【转载】进程间的通信之剪贴板方法实现源码
- linux扩充单个路径下的内存,超详尽!Linux云服务器存储扩容实操
- 基于智能计算的降维技术研究与应用
- HashMap深度解析:一文让你彻底了解HashMap
- display 隐藏css,CSS-元素的显示与隐藏
- 新建test.c为什么没有.h文件_新建STM32工程全局声明两个宏的原因
- 锁定计算机的软件,易通电脑锁(控制上网时间必备软件)
- 鼠标右键中没有新建选项,解决方法!
- Windows Phone 7 日历应用的实现
- R语言-混合型数据聚类
- github pages + Hexo + 域名绑定搭建个人博客增强版
- 关于EasyDarwinGo部署海康威视rtsp转hls视频多摄像头的服务器视频转码
- 毕业设计-基于 MATLAB 的图像分割算法研究及实现
- 给定一个完全循环赛的比赛结果,其中n个队伍两两比赛一次。每场比赛以一方胜出或者平局结束。设计一个算法,把n个队伍排序,序列中每个队伍都不曾输给紧随其后的那个队伍。说明该算法的时间效率类型。
- 微信小程序---霍兰德职业兴趣测试、心里测评、性格测评
- Android用Double Array Trie (双数组)实现关键字的搜索
- tekton TriggerTemplate资源
- 3星|《混乱》:案例比较有趣,推导过程和结论略牵强
- learnopengl 中 pbr的球体算法
热门文章
- iPhone手机通讯录如何导入另一部手机?
- 肯德基快餐店的收银系统
- ros 设置 catkin_make -j -l
- 两个助你高效编写 Kubernetes YAML 文件的神技
- c语言实验设计报告,c语言实验设计报告计科学号.doc
- 美团面试官:生成订单后一段时间不支付订单会自动关闭的功能该如何实现?越详细越好~
- Linux 查看进程、杀死进程
- html九宫格挖正方形代码,使用css伪类before/after实现 正方形三宫格、正方形六宫格、正方形九宫格...
- 苹果原装笔与其他触控笔有啥区别?Ipad好用触控笔推荐
- Vim 快捷键速查表