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

参考文献