C++ 11 及之后的版本中用 lambda 表达式(lambda 函数)来表示匿名函数,用于需要传递函数对象的参数,或者作为普通函数被调用。
匿名函数还可以使用外部作用域中的变量,这样就实现了闭包(closure):将函数和它的外部环境状态绑定在一起。
C++ 中匿名函数的结构如下:
- capture clause:需要从外部环境中捕获的变量,只有捕获的变量才能在 lambda 表达式内部使用
- parameter list:匿名函数的参数列表,与普通函数类似
- mutable
- exception
- trailing-return-type
- lambda body
Capture clause
声明 lambda 函数的 捕获变量列表,即通过怎样的方式(值传递、引用传递)捕获外部环境中的哪些变量。
捕获 的意思就是,只有在捕获变量列表中列举出的变量,才能在 lambda 表达式中访问其在外部环境中的值。
捕获可以分为 显式捕获 和 隐式捕获:
- 显式捕获:列出需要捕获的具体的变量名称,需要哪些就捕获哪些,
[var]
or[&var]
- 隐式捕获:捕获 lambda 函数所在词法上下文的所有变量,
[&]
or[=]
- 两种捕获方式可以一起使用
捕获方式可以分为 值传递 和 引用传递:
- 值传递:将捕获的外部变量的值传递到 lambda 内部,lambda 内部对变量的修改不会影响外部环境中的变量的值
- 引用传递:将捕获的外部变量的引用传递到 lambda 内部,内部的修改会影响到外部
具体例子:
[&x, y] // x 传递引用,y 传递值
[x, &y] // x 传递值,y 传递引用
[&, x] // 除了 x 传递值外,所有外部变量都传递引用
[=, &x] // 除了 x 传递引用外,所有外部变量都传递值
几个注意点:
-
同一个变量不能被多次声明捕获方式,例如
[i, i]
-
整体使用 引用传递 时不能再对具体变量使用引用传递,例如
[&, &i]
-
整体使用 值传递 时也不能再对具体变量使用值传递,例如
[=, i]
当在类的成员函数中使用 lambda 表达式时,可以捕获this
指针以在表达式中使用类的成员数据和方法。
this
是指针,它本身存储的是一个地址值,所以[=, this]
是不合法的,因为[this]
本身就是传递的地址值。
如果需要传递this
指向的对象的值(值传递),可以[=, *this]
。
C++ 14 中还可以在捕获列表中定义新变量(未在外部环境中声明的变量),按下不表。
Parameter list
lambda 函数的参数列表跟普通函数一样通过()
定义,只是在无参时可以将()
省略,变成[]{}
。
在 C++ 14 中,可以用auto
类型关键字声明参数类型为 泛型,一个表达式中的所有auto
类型会在编译时被替换成相同的类型模板。
auto add = [](auto x, auto y) {
return x + y;
}
Mutable specification
当外部变量通过 值传递 传递到表达式内部时,该变量默认为 常值(const),所以在表达式内部不允许修改通过 值传递 的变量。
要想解除这个禁用,需要显式指定mutable
关键字:
[=] () mutable { }
Exception specification
通过throw()
函数抛出异常:
[] () throw(int) {}
或者noexcept
说明表达式不会抛出异常:
[] () noexcept {}
Return type
一般情况下从已知类型变量计算出的返回值编译器可以自己推断返回值类型,不需要我们手动声明。
[] (int i) { return i * 2; }
对于不能明显推断出类型的返回值,需要手动声明 尾随的返回值类型(trailing-return-type):
[] () -> int { return 1; }
Lambda body
略。
评论区