lambda 表达式

LAMBDA简介[

lambda

](https://code.wandoer.com/wp-content/uploads/2016/01/images.jpeg)

从C++11开始,c++开始支持lambda表达式。

lambda用来创建一个可以捕获作用域内变量的匿名函数对象的闭包。这通常用来封装传递给算法或异步方法的少量代码块。

例如,简单实现一个Trim函数,去除字符串里的空格:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

string trimall(string str)
{
vector<char> vec;
for_each(str.begin(),str.end(),[&vec](char c){if(!isspace(c)) vec.push_back(c);});
return string(vec.begin(),vec.end());
}

int main() {
std::string str = " Hi, lam b da!";

std::cout << trimall(str);

return 0;
}

//OUTPUT:
//Hi,lambda!

此例中,

1
[&vec](char c){if(!isspace(c)) vec.push_back(c);}

即是一个lambda表达式。其做为一个匿名函数,被算法 *for_each *使用,使得代码更简洁。

语法

1
[ capture] ( params ) mutable exception attribute -> ret { body }

这是lambda的完整语法。在适当条件下,某些项可以忽略

  1. capture capture子句(也叫lambda引导符,“*lambda-introducer *”).

  2. params 参数列表(也叫lambda声明符,"lambda declarator"。可选)

  3. mutable 可变范围(可选)

  4. exception 异常规范(可选)

  5. ret 返回值类型(可选)

  6. body lambda体(“compound-statement”)

当某些项忽略时,存在以下的形式:

CAPTURE子句

lambda可以访问周边范围内的变量(外部变量),以capture子句开头指定要访问的变量及访问方式。以 “&”为前缀的变量为引用访问,没有该前缀的或以“=”为前缀的则为值访问。可以以使用默认访问模式访问外部变量,指定变量显式相反的访问模式。

  • [a,&b], 按值访问a,并按引用访问b

  • [&a,=], 按引用访问a,其它按值访问

  • [this], 按值访问this指针

  • [&], 按引用访问lambda体中使用的全部外部变量

  • [=], 按值访问lambda体中使用的全部外部变量

  • [ ], 不访问外部变量

在使用Capture子句时,以下几点需要注意:

  • 引用访问可用于修改外部变量,而值访问不可以

  • 引用访问的引入生存周期依赖项,而值访问没有生存周期依赖项。当lambda以异步方式运行时,这一点尤为重要。如果异步lambda中通过引用访问外部变量,则该变量可能在lambda运行时销毁,从而引起访问冲突。

**mutable **可变范围

对于值访问的外部变量,lambda的调用运算符为const-by-value。但是使用**mutable **可以使lambda能够改变通过值访问的外部变量。

1
2
3
4
5
6
7
8
9
10
11
//#include...
int main() {

int a = 0;
//[=]{++a;cout<<a;}(); //编译时错误。cannot assign to a variable captured by copy in a non-mutable lambda
[=]()mutable{++a;cout<<a;}();

return 0;
}
//OUTPUT:
// 1

上例中,可以使用"()"操作符让lambda直接执行,就像使用函数对象一样。

ret 返回值

如果没有指定返回值类型,lambda将自动推导返回值类型。如果指定返回值类型,则其必须位于参数列表之后,并使用"->"关键字。

1
2
3
4
5
6
7
8
9
//#include...
int main() {
int x = [](int a){return ++a;}(2); //OK
int y = [](int a)->int{return ++a;}(2); //OK
cout<<x<<","<<y;
return 0;
}
//OUTPUT:
//3,3
body lambda体

lambda和普通函数体基本相同。lambda体可以访问以下变量:

  • capture声明的外部变量

  • lambda体内声明的变量

  • 类的成员变量(当capture声明可以访问this时)

  • 具体静态持久存储的任何变量(如,静态变量、全局变量)

持久化

lambda可以简化代码,避免声明不常用的函数。然而再有些情况下需要将lambda持久化以多次使用。使用 *auto *关键字可以声明lambda类型的变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//include...
auto fPrintln = [](string str){cout<<str<<endl;};
int main() {
auto fAdd = [](int a,int b){return a+b;};
int x = fAdd(1,1);
cout<<x<<endl;

fPrintln("Hi wandoer.com");

return 0;
}
//OUTPUT:
//2
//Hi wandoer.com

参考资料