lambda表达式

什么是 Lambda?为什么要用它

Lambda(闭包)是 C++11 引入的语言特性,本质上是匿名函数对象(函数对象 = 重载 operator() 的类) 。它能方便地把一个小函数内联到代码里,常见用途包括:STL 算法回调(sort/transform/copy_if)、线程任务、事件回调、比较器、简短的自定义逻辑等。

语法骨架

[捕捉列表](参数列表) -> 返回类型 { 函数体 }

捕捉列表决定闭包如何获得外部数据(按值、按引用或捕捉 this)。


捕捉列表详解

  • [=]:按捕捉作用域内所有需要的外部变量(拷贝进闭包)。闭包内部的副本与外部变量独立,外部修改不影响闭包内部。

  • [&]:按引用捕捉作用域内所有外部变量。闭包内访问的是原变量,外部修改会反映进去。注意生命周期问题:如果闭包存活超过外部变量,引用会悬空(UB)。

  • [x] / [&x]:只捕捉指定变量(值或引用)。

  • [this]:捕捉当前对象指针(拷贝 this 指针)。闭包内部通过 this->member 访问成员,所以成员改变会反映。注意:如果闭包在对象销毁后仍被调用会 UB。若 lambda 在异步场景中延长生命周期,请确保对象生命周期足够或使用 shared_ptr/weak_ptr 等保护。

以下是代码示例:

cpp 复制代码
//(1)[=]
int x = 100, y = 200;
//以值传递的方式捕捉作用域内的所有变量
auto lam = [=]() {return x + y; };
cout << lam() << endl;//300
x++, y++;
cout << lam() << endl;//???
//结果说300,但x,y已经加+1了,为啥呢
//值传递,外面变了,里面不变,只传递了一次

//(2)[变量1,变量2]
//以值传递的方式捕捉特定变量
int x = 100;
auto lam = [x]()->int {return x * x; };
cout << lam() << endl;
x++;
cout << lam() << endl;

//(3)[&]
//以引用的方式,捕捉作用域内的所有变量
int x = 100;
auto lam = [&]()->int {return x * x; };
cout << lam() << endl;
x += 1;
cout << lam() << endl;

//4[&变量1,&变量2]
//以引用的方式捕捉作用域的特定方式
int x = 100, y = 200;
auto lam = [&x, &y]()->int {return x + y; };
cout << lam() << endl;
x++, y++;
cout << lam() << endl;


5[]不捕捉
auto add = [](int a, int b)->int 
{
	return a + b;
};
int x = add(3,5);
cout << x << endl;

//6[this]
 class A
{
public:
	void test()
	{
		//[this]到底是值传递,还是引用传递
		//貌似:值传递
		//实际上,也是值传递
		//但由于是传进来的是指针,所有变了
		auto func = [this]()->int {return a + b; };
		cout << func() << endl;//300
		a++;
		cout << func() << endl;//301
	}
private:
	int a = 100;
	int b = 200;
};
A a;
a.test();

//7[=,&]混合捕捉
int x = 100, y = 200;
auto lam=[=, &x]()->int { return x + y; };///先是全部值传递,然后覆盖原来的
cout << lam() << endl;
x++;
cout << lam() << endl;

auto lam = [=, x]()->int {return x + y; };//不可以,不能重复,可以覆盖

实践建议

  • 捕捉小对象或不可复制的资源时优先按值(安全);若按引用,一定保证被捕变量比闭包寿命更长或使用同步/智能指针。

  • 在类成员函数里用 [this] 很常见,但在异步/线程场景需小心对象生命周期。


Lambda 的底层:闭包类型与 decltype

每个 lambda 编译器生成一个匿名类(闭包类型),捕获的数据变成类成员,operator() 被重载用于调用。所以闭包是 "有类型 的对象",只是这个类型没有名字

cpp 复制代码
lambda表达式的底层实现是怎样的?
底层实现:就是c++的"函数对象"
函数对象是什么?
class A
{
public:
   int operator ()(int a,int b}
   {
      return a+b;
   }
}
A add;
cout<<add(3,5)<<endl;
这就是一个函数对象
让一个对象通过重载的方式实现函数

当你想把 lambda 类型作为模板参数(比如 std::set / std::map 的比较器类型),可以用 decltype(lambdaVar) 获取该闭包类型。例如:

cpp 复制代码
auto cmp = [](const auto& a, const auto& b){ return a.first > b.first; }; std::set<std::pair<int,int>, decltype(cmp)> st(cmp);

decltype(cmp) 是 lambda 的闭包类型;构造 st(cmp) 把 comparator 对象的实例传入容器(很多捕获了东西的闭包没有默认构造,所以必须在构造时提供实例)。

下面是lambda在STL与算法中的常见应用

cpp 复制代码
 1.STL容器与算法中的应用
 //transform
 int data[5] = { 1,2,3,4,5 };
	transform(data, data + 5, data, [](int x)->int {return x * x; });
	for (auto i : data) { cout << i << endl; }

	vector<int>num = { 1,2,3,4,5 };
	transform(num.begin(), num.end(), num.begin(), [](int x) {return x * 2; });
	for (auto i : num) { cout << i << endl; }

 //排序
 int data[5] = { 1,2,3,4,5 };
	sort(data, data + 1, [](int a, int b) {return a > b; });
	for (auto i : data)cout << i << " ";
	
	//STL中的算法回调
 vector<int>data1 = { 1,2,3,4,5 };
	vector<int>data2;
	copy_if(data1.begin(), data1.end(),back_inserter(data2), [](int x) {return x & 1; });
	for (auto i : data2)cout << i << endl;

 //并行编程(多线程编程)
 thread t1([]()
	{
		for (int i = 0; i < 10; i++) 
		{ 
cout << "thread1: " << i << endl; 
Sleep(1000);
		}
	});
 thread t2([]()
	{
		for (int i = 0; i < 10; i++) 
		{ 
cout << "thread2: " << i << endl; 
Sleep(1000);
		}
	});
	t1.join();
	t2.join();

递归 lambda:为什么不能直接在 lambda 内调用自身?怎么解决?

问题根源:lambda 的类型是匿名的,无法像普通函数那样在自身内部直接用名字调用(编译期没名字)。解决办法常见两种:

方法 A --- 传自身法(auto self

cpp 复制代码
auto fab = [](auto self, int n)->int
{
	if (n <= 2)return 1;
	return self(self, n - 2) + self(self, n - 1);
};
cout << fab(fab, 10) << endl;

优点:无运行时开销,编译器可做内联优化。

缺点:调用上略不直观(要传自己)。

方法 B --- std::function 捕捉法(先声明再赋)

cpp 复制代码
function<int(int)>fab = [&](int n)->int
{
	if (n <= 2)return 1;
	else return fab(n - 2) + fab(n - 1);
};
cout << fab(5) << endl;

优点:写法直观,可直接 fib(n) 调用;

缺点:std::function 有类型擦除和可能的堆分配开销,频繁调用时显著慢。

深入:为什么传自身法快,而 std::function 慢?

传自身法的优点

  • 都是编译期确定的类型(闭包类型 / 模板类型),调用可被编译器直接内联或做静态优化。

  • 无类型擦除、无间接函数指针跳转、无堆分配。

  • 调用开销接近普通函数调用(甚至更好,取决于内联)。

std::function 慢的原因

  • std::function类型擦除(type-erasure):它通过内部统一的调用接口(函数指针 + void*)来调用任意可调用对象。每次调用都要做一次间接调用(类似虚函数跳转),阻止内联优化。

  • 如果被包装对象不适合 Small-Buffer-Optimization(SBO),会发生堆分配,增加 malloc/free 开销。

  • 因为运行期抽象,编译器无法做很多静态优化(比如内联、常量传播等)。

结论 :性能敏感或高频递归场景用传自身或 ;若是一次性简便实现、性能不是关键点,std::function 可以接受。

下面介绍lambda在set和map的简单写法

cpp 复制代码
  lambda在set与map中的用法
  auto cmp = [](const pair<int,int>& a, const pair<int,int>& b)
	{
return a.first > b.first;
	};
	set<pair<int, int>,decltype(cmp)>st(cmp);
	st.insert({ 10,1 });
	st.insert({ 11,12 });
	st.insert({ 13,0 });
	for (const auto &item:st)cout <<item.first<< endl;
  
  auto cmp = [](const pair<int, int>& a, const pair<int, int>& b)
	{
	return a.first < b.first;
	};
	map<pair<int, int>, int, decltype(cmp)>mp(cmp);
	mp[{1, 4}]++;
	mp[{5, 1}]++;
	mp[{-1, 2}]++;
	for (auto item : mp)
	{
cout << item.first.first << endl;
	}
相关推荐
明洞日记1 小时前
【VTK手册017】 深入详解 vtkImageMathematics:医学图像的基本算术运算
c++·图像处理·算法·vtk·图形渲染
杰瑞不懂代码1 小时前
【公式推导】AMP算法比BP算法强在哪(一)
python·算法·机器学习·概率论
晚风(●•σ )1 小时前
C++语言程序设计——【算法竞赛常用知识点】
开发语言·c++·算法
浅川.251 小时前
xtuoj 哈希
算法·哈希算法·散列表
AndrewHZ2 小时前
【复杂网络分析】复杂网络分析技术在图像处理中的经典算法与应用实践
图像处理·人工智能·算法·计算机视觉·图像分割·复杂网络·图论算法
free-elcmacom2 小时前
机器学习入门<4>RBFN算法详解
开发语言·人工智能·python·算法·机器学习
java修仙传2 小时前
力扣hot100:最长连续序列
算法·leetcode·职场和发展
zore_c2 小时前
【C语言】文件操作详解3(文件的随机读写和其他补充)
c语言·开发语言·数据结构·笔记·算法
Pluchon2 小时前
硅基计划4.0 算法 记忆化搜索
java·数据结构·算法·leetcode·决策树·深度优先