“Lambda 表达式”是一个匿名函数,其基于数学中的 演算得名,直接对应于其中的Lambda抽象,是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包,但这一概念与数学上的闭包不同。本文旨在介绍Lamda表达式的语法规则以及在程序中的常规用法。
Lambda Expressions Syntax
演算可以被称为最小的通用程序设计语言。它包括一条变换规则 (变量替换) 和一条函数定义方式, 演算之通用在于,任何一个可计算函数都能用这种形式来表达和求值。
在 演算中,每个表达式都代表一个只有单独参数的函数,这个函数的参数本身也是一个只有单一参数的函数,同时,函数的值又是一个只有单一参数的函数。函数是通过 表达式匿名地定义的,这个表达式说明了此函数将对其参数进行什么操作。例如,函数 可以用 λ 演算表示为 。
在C++语言中,其语法规则如下:
▌语法形式[1]
1 | [captures] <tparams>(可选) (params) specifiers(可选) -> ret {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]:
图中标注的含义如下:
(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 | // captures_lambda_expression.cpp |
上述代码输出为:
5
0
由于n
为按值捕获的变量,所以当其响应完Lambda表达式后主体函数汇中的值仍为0。
下面的例子在STL中的generate_n
算法中借助Lambda表达式实现重设vector中任意元素为前两个元素的和。例子中使用了mutable
指定符使得表达式中拷贝的变量x
和y
可以被修改,但因为这两个变量是按值拷贝的,所以在表达式执行结束之后,变量x
和y
仍然为1。
1 | // compile with: /W4 /EHsc |
上述代码输出为:
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