C++中的Lambda表达式

关于C++中的Lambda表达式.

0 语法

1
2
3
[capture list] (params list) mutable exception -> return type {
function body
}
  • capture list: 捕获外部变量列表
  • params list: 形参列表
  • mutable: 指示是否可以修改捕获的变量
  • exception: 异常设定
  • return type: 返回类型
  • function body: 函数体

其中,某些成分可省略,常见形式包括

1
2
3
[capture list] (params list) -> return type {function body}
[capture list] (params list) {function body}
[capture list] {function body}

1 一些简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 创建为可调用对象(callable object)
// 省略参数列表
auto func = [] {cout << "func" << endl;};
func();

// 指明返回类型
auto add = [](int a, int b) -> int {return a + b;};
int sum = add(1, 2);

// 自动推断返回类型
auto mul = [](int a, int b) {return a * b;};
int prod = mul(1, 2);

// 作为比较器
sort(v.begin(), v.end(), [](int a, int b) -> bool {return a < b;});

2 捕获外部变量

简单来说,”捕获”的意思是,lambda表达式可以使用其可见范围内的外部变量,但必须明确声明(即声明在方括号[]中).

1
2
3
int x = 1;
auto func = [x](int a) {return a + x};
int res = func(2); // res == 3

大家可能会想lambda表达式最前面的方括号的意义何在?其实这是lambda表达式一个很要的功能,就是闭包. 这里我们先讲一下lambda表达式的大致原理:每当你定义一个lambda表达式后,编译器会自动生成一个匿名类(这个类当然重载了()运算符),我们称为闭包类型(closure type). 那么在运行时,这个lambda表达式就会返回一个匿名的闭包实例,其实一个右值. 所以,我们上面的lambda表达式的结果就是一个个闭包. 闭包的一个强大之处是其可以通过传值或者引用的方式捕捉其封装作用域内的变量,前面的方括号就是用来定义捕捉模式以及变量,我们又将其称为lambda捕捉块.

来源:https://www.jianshu.com/p/d686ad9de817

2.1 值捕获

创建lambda表达式时,被捕获的变量通过值拷贝的方式传入. 因此后续对该变量的改变,不会影响lambda函数中的该变量.

1
2
3
4
int x = 1;
auto func = [x] {cout << x << endl;};
x = 2;
func(); // output: 1

2.2 引用捕获

同理,按引用将变量传入.

1
2
3
4
int x = 1;
auto func = [&x] {cout << x << endl;};
x = 2;
func(); // output: 2

2.3 隐式捕获

依靠编译器来推断需要捕获的变量. 有按值捕获或按饮用捕获.

  • 隐式值捕获 [=]

    1
    2
    3
    4
    int x = 1;
    auto func = [=] {cout << x << endl;};
    x = 2;
    func(); // output: 1
  • 隐式引用捕获 [&]

    1
    2
    3
    4
    int x = 1;
    auto func = [&] {cout << x << endl;};
    x = 2;
    func(); // output: 2

2.4 修改捕获变量

使用mutable关键字.

1
2
3
int x = 1;
auto func = [x](int a) mutable {x *= 2; return a * x;};
int res = func(2); // res == 4

2.5 混合捕获方式

捕获形式 说明
[] 不捕获任何外部变量
[args, …] 默认以值得形式捕获指定的多个外部变量(用逗号分隔)
[=] 默认以值捕获所有变量
[&] 默认以引用捕获所有变量
[=, &x] 默认以值捕获所有变量,但是x是例外,通过引用捕获
[&, x] 默认以引用捕获所有变量,但是x是例外,通过值捕获
[this] 通过引用捕获当前对象(其实是复制指针)
[*this] 通过传值方式捕获当前对象

参考

[1] 博客园 - C++ 11 Lambda表达式

[2] 简书 - C++ lambda表达式与函数对象

[3] C++ Primer, 5th Edition