“Lambda 表达式”是一个匿名函数,其基于数学中的 λ\lambda 演算得名,直接对应于其中的Lambda抽象,是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包,但这一概念与数学上的闭包不同。本文旨在介绍Lamda表达式的语法规则以及在程序中的常规用法。

Lambda Expressions Syntax

λ\lambda 演算可以被称为最小的通用程序设计语言。它包括一条变换规则 (变量替换) 和一条函数定义方式, λ\lambda 演算之通用在于,任何一个可计算函数都能用这种形式来表达和求值。

λ\lambda 演算中,每个表达式都代表一个只有单独参数的函数,这个函数的参数本身也是一个只有单一参数的函数,同时,函数的值又是一个只有单一参数的函数。函数是通过 λ\lambda 表达式匿名地定义的,这个表达式说明了此函数将对其参数进行什么操作。例如,函数 f(x)=x+2f(x)=x+2 可以用 λ 演算表示为 λ  x.x+2\lambda\ \ x.x+2

在C++语言中,其语法规则如下:

▌语法形式[1]

1
2
3
4
[captures] <tparams>(可选) (params) specifiers(可选) -> ret {body}
[captures] (params) -> ret {body}
[captures] (params) {body}
[captures] {body}

上述语法中:
(1) Lambda表达式的完整声明。
(2) 声明了const类型的表达式,这种类型的表达式不能修改捕获列表中的值。
(3) 省略了返回值类型,但编译器可以根据以下规则推断出Lambda表达式的返回类型:
- 如果函数体中存在return语句,则返回类型由return语句的返回类型确定;
- 如果函数体中没有return语句,则返回值为void类型.
(4) 省略参数列表:函数不接收参数,即参数列表为 (),类似于普通函数中的无参函数。

▌语法解释

参数 解释
captures 表达式捕获外部参数列表,其有如下形式:
[];[&];[=];[=,&foo];[bar];[this]
<tparams> 模板形参列表(于角括号中),用于提供名称给泛型表达式的模板形参
params 形参列表
specifiers 可选的指定符序列。允许下列指定符:
mutable:允许 body 修改已复制的捕获参数,及调用其non-const 成员函数
exception:说明表达式是否抛出异常以及何种异常
attribute:用来声明属性
ret 返回类型,若缺失,则由函数的return语句推断返回值类型(或若函数不返回任何值则为void
body 匿名函数体

Properties of Lambda Expressions

下图映射了上文所介绍的Lambda表达式的语法规则[2]
Lambda语法


图中标注的含义如下:
(1) 表达式捕获外部参数列表;
(2) 形参列表;
(3) 可选的指定符序列:可变指定符;
(4) 可选的指定符序列:异常指定符;
(5) 返回类型;
(6) 表达式的匿名函数体。

这里需要详细介绍捕获外部参数列表中参数的几种形式,下文例子中体现了这一部分的内容[3]
[]:没有使用任何函数对象参数,不捕获任何对象。
[=]:函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
[&]:函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
[this]:函数体内可以使用Lambda所在类中的成员变量。
[bar]:将bar按值进行传递。按值进行传递时,函函数体内不能修改传递进来的bar的拷贝,因为默认情况下函数是const的。要修改传递进来的bar的拷贝,可以添加mutable修饰符。
[&foo]:将foo按引用进行传递。
[bar,&foo]:将bar按值进行传递,foo按引用进行传递。
[=,&a,&b]:除a和b按引用进行传递外,其他参数都按值进行传递。
[&,a,b]:除a和b按值进行传递外,其他参数都按引用进行传递。

Examples

Lambda函数体可访问其所在主体函数的如下类型的变量:
- 参数
- 本地声明的变量
- 在类内声明的数据成员
- 任何具有静态存储时间的变量(如全局变量)

没有在Lambda表达式的捕获子句中但出现在Lambda函数体中的变量为 隐式捕获,否则为 显式捕获,下面的例子中包含了一个显示捕获的变量n和隐式捕获的变量m

1
2
3
4
5
6
7
8
9
10
11
12
// captures_lambda_expression.cpp
// compile with: /W4 /EHsc
#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;
}

上述代码输出为:

5
0

由于n为按值捕获的变量,所以当其响应完Lambda表达式后主体函数汇中的值仍为0。

下面的例子在STL中的generate_n算法中借助Lambda表达式实现重设vector中任意元素为前两个元素的和。例子中使用了mutable指定符使得表达式中拷贝的变量xy可以被修改,但因为这两个变量是按值拷贝的,所以在表达式执行结束之后,变量xy仍然为1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// compile with: /W4 /EHsc
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>

using namespace std;

template <typename C> void print(const string& s, const C& c) {
cout << s;

for (const auto& e : c) {
cout << e << " ";
}

cout << endl;
}

void fillVector(vector<int>& v)
{
// A local static variable.
static int nextValue = 1;

// The lambda expression that appears in the following call to
// the generate function modifies and uses the local static
// variable nextValue.
generate(v.begin(), v.end(), [] { return nextValue++; });
//WARNING: this is not thread-safe and is shown for illustration only
}

int main()
{
// The number of elements in the vector.
const int elementCount = 9;

// Create a vector object with each element set to 1.
vector<int> v(elementCount, 1);

// These variables hold the previous two elements of the vector.
int x = 1;
int y = 1;

// Sets each element in the vector to the sum of the
// previous two elements.
generate_n(v.begin() + 2,
elementCount - 2,
[=]() mutable throw() -> int { // lambda is the 3rd parameter
// Generate current value.
int n = x + y;
// Update previous two values.
x = y;
y = n;
return n;
});
print("vector v after call to generate_n() with lambda: ", v);

// Print the local variables x and y.
// The values of x and y hold their initial values because
// they are captured by value.
cout << "x: " << x << " y: " << y << endl;

// Fill the vector with a sequence of numbers
fillVector(v);
print("vector v after 1st call to fillVector(): ", v);
// Fill the vector with the next sequence of numbers
fillVector(v);
print("vector v after 2nd call to fillVector(): ", v);
}

上述代码输出为

vector v after call to generate_n() with lambda: 1 1 2 3 5 8 13 21 34
x: 1 y: 1
vector v after 1st call to fillVector(): 1 2 3 4 5 6 7 8 9
vector v after 2nd call to fillVector(): 10 11 12 13 14 15 16 17 18






  1. cppreference.Lambda 表达式.Link ↩︎

  2. 微软帮助中心.Lambda Expression Syntax.Link ↩︎

  3. 糖炒栗子Sugar.C++ Lambda表达式用法.Link ↩︎