Lambda 表达式是重载了函数调用运算符 () 的未命名类的未命名对象。
Lambda 表达式可以被认为是一个匿名的函数对象。
与任何函数类似一个 Lambda 表达式包含一个返回类型、一个参数列表、一个函数体。
01 Lambda 定义
[captuer_list](parameter_list) -> return_type { function_body }
captuer_list是捕获列表,是一个 Lambda 表达式所在函数中定义的局部变量的列表return_type是尾置返回,是一个 Lambda 表达式的返回类型,如果想要显式的声明返回值的类型则必须使用这种形式Lambda表达式必须包含捕获列表和函数体。
02 向 Lambda 表达式传递参数
默认情况下,从 Lambda 表达式生成的类都包含一个对应该 Lambda 表达式所捕获的变量的数据成员。
类似任何普通类的数据成员, Lambda 表达式的数据成员也在 Lambda 表达式对象创建时被初始化,即被捕获的变量值是在 Lambda 表达式创建时拷贝,而不是调用时拷贝。
void func() {
int n = 10;
auto func = [n]() { return n; }; // n 在创建时被拷贝到 Lambda 表达式中
n = 0; // 改变外部的值不会影响到 Lambda 中的拷贝
auto m = func(); // 调用时 Lambda 表达式中 n 的拷贝的值为 10
cout << m << endl; // 10
}
Lambda 表达式不能有默认参数,Lambda 表达式调用的实参数目永远和形参数目相等。
Lambda 的捕获方式
| 语法 | 说明 |
|---|---|
| [] | 空捕获列表。Lambda 不能使用所在函数中的变量 |
| [names] | names 是一个以 , 分隔开的名字列表,这些名字的都是 Lambda 表达式所在函数的局部变量。默认情况下捕获列表中的变量都是拷贝 |
| [&] | 隐式捕获列表,采用引用捕获方式。Lambda 表达式中所使用的来自所在函数的实体均采用引用的方式 |
| [=]` | 隐式捕获列表,采用值捕获方式。Lambda 表达式中所使用的来自所在函数的实体均采用值传递的方式 |
| [&, identifier_list] | identifier_list 是一个 , 分隔的列表,包含 0 个或多个来自所在函数的变量,且这些变量均采用值捕获方式,而任何隐式捕获的变量都采用引用方式捕获, identifier_list 中的名字前面不能再使用 & |
| [=, identifier_list] | identifier_list 是一个 , 分隔的列表,包含 0 个或多个来自所在函数的变量,且这些变量均采用引用捕获方式,而任何隐式捕获的变量都采用引用方式捕获, identifier_list 中的名字不能包含 this 且这些名字之前必须使用 & |
- 隐式捕获是为了让编译器根据 Lambda 体中的代码来推断可能要使用到的变量。
- 当混合使用隐式捕获和显式捕获时,捕获列表中的第一个元素必须是
&或=, 此符号制定了默认捕获方式的类型。 - 当混合使用隐式捕获和显式捕获时,显式捕获的变量必须使用与隐式捕获不同的方式。
// 捕获举例 void func() { int n = 10; // 值捕获 auto func1 = [n]() { return n; }; // 引用捕获 auto func2 = [&n]() { return n; }; n = 0; auto p = func1(); // p = 10 auto q = func2(); // q = 0 }
03 可变 Lambda
-
默认情况下,对于一个值被拷贝的变量, Lambda 不会改变其值。
如果我们希望改变一个被捕获的变量的值,就必须再参数列表首加上关键字mutable, 且可变 Lambda 能省略参数列表。void func() { int n = 10; auto func1 = [n]() { return ++n; }; // error: 无法在非可变 lambda 中修改通过复制捕获 auto func2 = [n]() mutable { return ++n; }; // ok } -
引用捕获的变量是否可以修改取绝于此引用指向的是一个
const还是一个非const类型。void func() { int n = 10; const int m = 81; auto func1 = [&n]() { return ++n; }; auto func2 = [&m]() { return ++m; }; // error: 无法在非可变 lambda 中修改通过复制捕获 auto func3 = [&m]() mutable { return ++m; }; // error: 左值指定 const 对象 }
04 指定 Lambda 返回类型
默认情况下,如果一个 Lambda 体包含 return 之外的任何语句,则编译器会假定此 Lambda 体返回 void.
当需要为 Lambda 定义返回类型时,必须使用尾置返回类型。
int n = -10;
auto func1 = [](int i) { return i > 0 ? i : -i; };
auto func2 = [](int i) { if (i > 0) return i; else return -i; };
auto func3 = [](int i) -> decltype(i) { if (i > 0) return i; else return -i; };
05 使用 std::function 存储 Lambda 表达式
std::function 可以存储 Lambda 表达式。
std::function 还可以绑定普通函数、静态成员函数和共有成员函数。前两者的使用方法和绑定 Lambda 表达式的方法一样:直接将函数名赋值给 function 对象即可;但类的成员函数需要使用 bind 绑定:
MyClass *obj = new MyClass();
// 使用 bind 绑定成员函数和对象指针
// 使用 placeholders 占位符来表示函数的参数数量,其后缀一次为 1~N
std::function<void(int)> func1 = std::bind(&MyClass::testFunc1, obj, std::placeholders::_1);
std::function<void(int, char)> func2 = std::bind(&MyClass::testFunc2, obj, std::placeholders::_1, std::placeholders::_2);
本文深入解析C++中的Lambda表达式,包括定义、参数传递、可变Lambda、返回类型指定及std::function的应用。通过实例展示不同捕获方式的效果。
916

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



