Lambda表达式
1. 概述
Lambda表达式是现代编程语言的一个特点,他有如下优点:
- 声明式的编程风格:就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或函数对象。
- 简洁:避免了代码膨胀和功能分散,然开发更加高效。
- 灵活:在需要的时间和地点实现功能闭包,使得程序更加灵活。
语法:
[capture](params) opt -> ret {body;};
capture
:捕获列表,用于声明要捕获变量的范围。params
:参数列表,和普通的参数列表一样,没有就空着。opt
:功能选项,可以省略,包括两种:mutable
:可以修改按值传进来的拷贝。使用时有两点要注意:- 修改的是传进来的拷贝,外面的变量本身没有被修改。
- 使用时前面必须加上参数列表,即使没有参数页要写。
exception
:指定函数抛出的异常。
- 返回值类型:在C++11中,Lambda表达式的返回值是通过返回值后置语法来定义的。(它和
->
符号同生共死) - 函数体:函数实现。
2. 捕获列表
主要用于指定捕获变量的范围。具体方式如下:
[]
:不捕获任何变量。[=]
:按值捕获。即捕获外界作用域的所有变量,并作为副本在函数体内使用。(拷贝的副本在匿名函数内部默认是只读的,除非在opt
中指定muteale
,但即使修改,修改的也只是副本,外界变量本身并没有变。)[&]
:按引用捕获。即捕获外界作用域的所有变量,并作为引用在函数体内使用。(可以修改外部变量的值)[=, &temp]
:按值捕获外界作用域所有的变量,同时按引用捕获temp
变量。[temp]
:按值捕获temp
变量,不捕获其余变量。[&temp]
:按引用捕获temp
变量,不捕获其余变量。[this]
:捕获当前类中的this
指针。- 让Lambda和当前类的成员函数拥有同样的访问权限。一般来说就是为了访问成员变量。
- 可以通过
this
指针修改外部变量的值。 - 如果已经使用了
&/=
,那么默认添加该选项。
EG:
#include <iostream>
using namespace std;
class Test
{
private:
int num;
public:
void output(int x, int y)
{
auto x1 = [] {return num; }; // error
auto x2 = [=] {return num + x + y; }; // ok
auto x3 = [&] {return num + x + y; }; // ok
auto x4 = [this] {return num; }; // ok
auto x5 = [this] {return num + x + y; }; // error
auto x6 = [this, x, y] {return num + x + y; }; // ok
auto x7 = [this] {return num++; }; // ok
}
};
x1
:错误。没有捕获任何变量,因此无法访问num。x2
:正确。按值捕获外部作用域的变量(注意:默认是进行拷贝)。x3
:正确。按引用捕获外部作用于的变量。x4
:正确。获取this
指针,可访问类内部对象。x5
:错误。获取this
指针,可访问类内部对象,但无法访问函数的参数列表。x6
:正确。获取this
指针,可访问类内部对象。同时按值获取x
、y
变量。x7
:正确。可以通过this
指针修改外部变量的值。
3. 返回值
大多数时候,函数的返回值类型非常明显,因此C++11允许在Lambda中忽略函数的返回值类型。
-
完整的Lambda表达式:
auto f = [](int a) -> int {return a;};
-
忽略返回值类型:
auto f = [](int a) {return a;};
注意:
一般c情况下Lambda可以自动推导出返回值类型,但无法通过列表初始化自动推导出返回值类型。
auto f = []{
return {1, 2}; // 这样会发生错误
};
4. 函数本质
使用lambda表达式捕获列表捕获外部变量,如果希望去修改按值捕获的外部变量,那么应该如何处理呢?这就需要使用mutable选项,被mutable修改是lambda表达式就算没有参数也要写明参数列表,并且可以去掉按值捕获的外部变量的只读(const)属性。
int a = 0;
auto f1 = [=] {return a++; }; // error, 按值捕获外部变量, a是只读的
auto f2 = [=]()mutable {return a++; }; // ok
为什么按值拷贝得到的变量默认是只读的?
- lambda表达式的类型在C++11中有两种情况:
- 没有捕获任何变量:可以转化为一个函数指针。
- 捕获了外界变量,会被看做是一个带operator()的类,即仿函数。而按照C++标准,lambda表达式的
operator()
默认是const
的,一个const
成员函数是无法修改成员变量值的。
mutable选项的作用就在于取消operator()的const属性。
因为lambda表达式在C++中会被看做是一个仿函数,因此可以使用std::function
和std::bind
来存储和操作lambda表达式:
#include <iostream>
#include <functional>
using namespace std;
int main(void)
{
// 包装可调用函数
std::function<int(int)> f1 = [](int a) {return a; };
// 绑定可调用函数
std::function<int(int)> f2 = bind([](int a) {return a; }, placeholders::_1);
// 函数调用
cout << f1(100) << endl;
cout << f2(200) << endl;
return 0;
}
对于没有捕获任何变量的Lambda表达式,还可以转化为一个普通的指针函数:
using func_ptr = int(*)(int);
func_ptr p1 = [](int a) {
return a;
};
p1(123);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能