Lambda函数简说(不讲类中的使用)

一.诠释

在 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,比较方便。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值