lambda表达式是一个来源于函数式编程的概念, 在C++11中加入.
lambda表达式可以就地定义匿名的定义函数, 不需要单独写一个函数或者函数对象(仿函数, 如果有状态), 代码可读性更高. 可以和函数对象一样实现闭包.
lambda的基本功能
lambda表达式的语法形式:
[捕获列表](参数)函数选项 -> 返回值类型 {函数体};
更严谨一些的描述
1.capture 子句 (C++ 规范中也称为 lambda 引入器 。)
2.参数列表 (可选)。 (也称为 lambda 声明符)
3.可变规范 (可选)。
4.exception-specification 选。
5.trailing-return-type 选。
6.lambda 正文。
举例
auto f = [](int a) -> int {return a + 1;};
auto result = f(1); // result = 2
返回值类型再可以自动推导时可以省略 此时的表达式:
auto f = [](int a){return a+1;};
类似的 如果表达式参数列表 那参数列表也可以省略
auto f = []{return 0;};
捕获列表
[] 不捕获任何变量。
[&] 捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。
[=] 捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。
[=,&foo] 按值捕获外部作用域中所有变量,并按引用捕获 foo 变量。
[bar] 按值捕获 bar 变量,同时不捕获其他变量。
[this] 捕获当前类中的 this 指针,让 lambda 表达式拥有和当前类成员函数同样的访问权限。如果已经使用了 & 或者 =,就默认添加此选项。捕获 this 的目的是可以在 lamda 中使用当前类的成员函数和成员变量。
表达式捕获外部变量之后, 如果是按值捕获的, 那么在lambda表达式中修改对应的变量不会影响外部变量, 如果希望能够修改外部变量, 需要使用按引用捕获.
demo
class A
{
public:
int i_ = 0;
void func(int x, int y)
{
auto x1 = []{ return i_; }; // error,没有捕获外部变量
auto x2 = [=]{ return i_ + x + y; }; // OK,捕获所有外部变量
auto x3 = [&]{ return i_ + x + y; }; // OK,捕获所有外部变量
auto x4 = [this]{ return i_; }; // OK,捕获this指针
auto x5 = [this]{ return i_ + x + y; }; // error,没有捕获x、y
auto x6 = [this, x, y]{ return i_ + x + y; }; // OK,捕获this指针、x、y
auto x7 = [this]{ return i_++; }; // OK,捕获this指针,并修改成员的值
}
};
int a = 0, b = 1;
auto f1 = []{ return a; }; // error,没有捕获外部变量
auto f2 = [&]{ return a++; }; // OK,捕获所有外部变量,并对a执行自加运算
auto f3 = [=]{ return a; }; // OK,捕获所有外部变量,并返回a
auto f4 = [=]{ return a++; }; // error,a是以复制方式捕获的,无法修改
auto f5 = [a]{ return a + b; }; // error,没有捕获变量b
auto f6 = [a, &b]{ return a + (b++); }; // OK,捕获a和b的引用,并对b做自加运算
auto f7 = [=, &b]{ return a + (b++); }; // OK,捕获所有外部变量和b的引用,并对b做自加运算
lambda表达式的类型
lambda表达式为闭包类型(Closure Type).
如果lambda表达式没有捕获任何变量(即没有状态), 可以被转换为函数指针.
int (*func_t)(int);
func_t f = [](int a){return a;};
如果捕获了变量, 可以认为得到了一个函数对象(同样拥有operator()). 同样也可以配合 std::function 和 std::bind 使用.
mutable
#include <iostream>
using namespace std;
int main()
{
int m = 0;
int n = 0;
[&, n] (int a) mutable { m = ++n + a; }(4);
cout << m << endl << n << endl;
}
代码中对捕获得到的n进行了自增操作, 如果没有mutable关键字 因为operator() const 的限制, 表达式不允许对对象内的变量进行修改, mutable关键字去掉了这一限制.
微软的文档中这样描述:
通常,lambda 的函数调用运算符是常量值,但使用 mutable 关键字会取消此操作。它不生成可变数据成员。 该 mutable 规范使 lambda 表达式的正文能够修改值捕获的变量。1