一.诠释
在 C++ 中,Lambda 表达式(也称为匿名函数或闭包)是一种用于快速定义和创建匿名函数对象的语法特性。它允许你在需要函数的地方内联编写函数逻辑,而无需预先声明一个命名函数。Lambda 表达式在 C++11 标准中引入,并逐渐增强功能(C++14、C++17、C++20 等)。
二.模板
1.公式
[捕获变量](参数列表)可选限定符->返回类型{
//函数代码
}
2.例子
头文件我瞎写的,其实只要iostream就行
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <functional>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
//Lambda表达式示例
using namespace std;
int main(void){
//示例1
cout<<"示例1"<<endl;
int x = 7;
float y= 3.0;
struct{
int _x;
float _y;
float operator()(int a , int b) const
{
return _x*_y+a*b;
}
}p{x,y};
cout<<p(10,20)<<endl;
//示例2
cout<<"示例2"<<endl;
vector<float> numbers{1.1,2.0,3.5,-1.4,29.2,12.1,33,6,0};
sort(numbers.begin(),numbers.end(),[](float a,float b){return a<b;});
for(auto v:numbers)
cout<<v<<" ";
cout<<endl;
//示例3
cout<<"示例3"<<endl;
x = 7;
y = 3.0;
auto pp = [x,y](int a , int b)->float{
return x*y+a*b;
};
cout<<pp(10,20)<<endl;
//示例4
cout<<"示例4" <<endl;
x = 7;
y = 3.0;
auto p1 = [x,y](int a , int b)->float{
return x*y+a*b;
};
cout<<p1(10,20)<<endl;
y+=1.5;
cout<<p1(10,20)<<endl;
//示例5
cout<<"示例5"<<endl;
x = 7;
y = 3.0;
// 第一次定义 Lambda(捕获 x=7, y=3.0)
function<float(int,int)> ppp = [x, y](int a, int b) -> float {
return x * y + a * b;
};
cout << ppp(10, 20) << endl; // 输出 221(7*3.0 + 10*20)
y += 1.5; // y 变为 4.5
// 重新定义 Lambda(捕获 x=7, y=4.5)
ppp = [x, y](int a, int b) -> float {
return x * y + a * b;
};
cout << ppp(10, 20) << endl; // 输出 231.5(7*4.5 + 10*20)
//示例6
cout<<"示例6"<<endl;
x = 7;
y = 3.0;
auto pppp = [&x, &y](int a, int b) -> float {
return x * y + a * b;
};
cout << pppp(10, 20) << endl; // 输出 221(7*3.0 + 10*20)
y += 1.5; // y 变为 4.5
cout << pppp(10, 20) << endl; // 输出 231.5(7*4.5 + 10*20)
//示例7
cout<<"示例7"<<endl;
x = 7;
y = 3.0;
float z = -1;
auto p2 = [=](int a , int b)->float{
return x*y+a*b;
};
cout<<p2(10,20)<<endl;
//示例8
cout<<"示例8"<<endl;
x = 7;
y = 3.0;
z = -1;
auto p3 = [=,&y](int a , int b)->float{
return x*y+a*b;
};
cout<<p3(10,20)<<endl;
//示例9
cout<<"示例9"<<endl;
x = 7;
y = 3.0;
z = -1;
auto p4 = [&,y](int a , int b)->float{
return x*y+a*b;
};
cout<<p4(10,20)<<endl;
//示例10
cout<<"示例10"<<endl;
float k = 1.0;
float b = 2.0;
const int N = 10;
static float m = 2.5;
auto f = [k,b](float x){
return k*x+b+N+m;
};
cout<<f(3)<<endl;
return 0;
}
实例一:
这里并没用Lambda函数,但是 Lambda 的底层就是用匿名结构体 + operator() 实现的。
p后面为什么接了个花括号(大括号)({})?p{x, y} 的作用就是直接把外部的 x 和 y 的值,按顺序赋值给匿名结构体内部的 _x 和 _y。可以理解为“按声明顺序填坑”,简单直接。
实例二:
用在了sort函数中,更加灵活,这样可以自己设定排序方式,和在外面写函数调用到里面是一样的,不过这样写更加方便。
示例三:
很典型的一个实例,cout这一行就是把10,20的值传了进去给a,b,跟函数差不多,【】捕获了外面的x,y这两个变量,注意返回值是float!
注意Lambda函数后面得加;因为这里是把一行代码给拆开了,跟结构体类似,在sort函数里是因为只是一个语句的一部分,不需要加分号,语句未说完。
示例四:
一个很典型但是是个错误示例的示例,很多人天真的认为我直接加个1.5不就改变值了吗?实则不然,这种会使值一模一样,因为Lambda 表达式在捕获 x 和 y 时是按值捕获([x, y]),捕获的是它们当时的副本,后续外部 y 的修改不会影响 Lambda 内部已经捕获的副本。
示例五:
很多人又天真的认为,我在下面再次声明一下不久完事了吗,实则不然,因为C++ 中,每个 Lambda 表达式的类型是唯一且不可复制的(即使是相同的写法,也会生成不同的类型)。
ppp 的类型在第一次定义时已经固定,不能直接赋值为另一个 Lambda。所以需要像我一样,在前面加个function,就可以了。又有人会说,为什么不用auto了呢,因为auto 会让编译器为每个 Lambda 生成唯一的匿名类型,即使两个 Lambda 的代码完全相同,它们的类型也不同。因此,第二次赋值时,类型不匹配,导致编译错误。换而言之,第一个 Lambda 的实际类型可能是 __Lambda1,第二个是 __Lambda2,二者无法直接赋值。那为什么后面的类型是<float(int,int)>呢,因为float(int, int) 是函数签名,表示这个 function 可以包装任何接受两个 int 参数并返回 float 的可调用对象(如 Lambda、函数指针等)。但需要注意,function有头文件:
#include <functional>//不是function,需要加形容词后缀变为functional
示例六:
再推荐一种,直接加个&号,表明在外面可以更改捕获值,这样在Lambda函数内也可以更改捕获变量值,这种在函数内部的引用叫做隐式捕获,但注意,这是隐式的引用捕获,没有隐式按值捕获。想要改变里面的值,可以这么写:
auto pppp = [&x, &y](int a, int b) mutable-> float {
x++ , y++;
return x * y + a * b;
};
这样就可以改值了。
有人会问这么写可不可以:
int x = 10;
int &ref = x;
这样写只能代表x随着ref的值变化而变化(同根生),至于下面,就不行了。
示例7
再介绍一个投机取巧的东西,【=】(默认按值捕获),这个是啥意思,比如说你Lambda函数里面有114514个值,你想按值捕获(就是里面不能改值,不带&),一个一个写就会很麻烦,于是这个东西就直接帮助你把这些变量全部都代进去,大大节省时间和空间。至于你后面单独写引用捕获或是显示捕获,那不管你,它只是把你没有特殊声明的需要捕获的变量,全部都给按值捕获,非常方便
示例8
我已经说了,就是你这里特殊说明了,y是引用捕获,所以这个=只是把x按值捕获,没有y的事,其余同理。
示例9
这个就是和我刚才说的【=】差不多,这个是【&】(默认引用捕获),功能同理,不多说了。
注意注意注意!!!!!!
这样是通过不了的:
x = 7;
y = 3.0;
z = -1;
auto p4 = [&,&y](int a , int b)->float{
return x*y+a*b;
};
cout<<p4(10,20)<<endl;
x = 7;
y = 3.0;
z = -1;
auto p3 = [=,y](int a , int b)->float{
return x*y+a*b;
};
cout<<p3(10,20)<<endl;
这个弊端就是不能重复说明同一个变量捕获
auto createLambda()
{
float x = 1.0;
float y = 2.0;
return [&](float a){return a*x+y;};
}
int main(void)
{
auto f = createLambda();
cout<<f(2.0)<<endl;
return 0;
}
这里是应用捕获,但由于函数内声明的变量在出函数时,就销毁了,所以出现了严重的悬空引用问题,这样会导致程序危险且不可靠,如果想改正,建议改为:
auto createLambda()
{
float x = 1.0;
float y = 2.0;
return [=](float a){return a*x+y;};
}
int main(void)
{
auto f = createLambda();
cout<<f(2.0)<<endl;
return 0;
}
这样Lambda函数内部会有自己独立的副本,能够支撑函数正常运行。
为什么你的代码能编译但不保证运行正确?
-
C++ 编译器不会检查悬空引用问题(这是运行时的未定义行为)。
-
可能“偶然”输出正确结果(内存未被覆盖时),但这是不可靠的!
永远不要返回持有局部变量引用的 Lambda!
如需引用捕获,必须严格管理变量的生命周期。
示例10
在捕获时,除了可以在函数体内部使用捕获变量,还可以直接使用自定义变量(比如结构体),静态变量(static),全局变量,常量(const,注意不能更改),这些都是不需要捕获的。
三.后记
不用类的情况下,Lambda函数需要c++14,并且()内的值的类型可以是auto,比较方便。

1060

被折叠的 条评论
为什么被折叠?



