lambda 表达式有时也称为 lambda 函数,或者直接简称为 lambda ;它是定义和使用匿名函数对象的一种简便的方式。
一条 lambda 表达式包含以下组成要件:
[]
内。()
内。->
形式的返回类型声明。{}
内。即,一个 lambda 表达式具有如下形式:
[捕获列表](参数列表)->返回值{表达式体};
在 lambda 表达式的概念中,传参、返回结果以及定义表达式体等环节都与函数的相应概念是一致的。
区别在于,函数没有提供局部变量“捕获”的功能;这意味着 lambda 表达式可以作为局部函数使用,而普通函数不能。
lambda 表达式是一种局部类类型,它含有一个构造函数以及一个 const 成员函数 operator()()。
lambda 表达式不仅可以作函数实参,还能用于初始化一个声明为 auto 或者std::function
的变量。其中,R 是 lambda 的返回类型,AL是它的类型参数列表。
lambda 表达式作函数实参
vector<int> v{ 1,3,3,7,9 };
for_each( v.begin(), v.end(), [](int& x){x += 1;} ); //每个元素都加一
lambda 表达式用于初始化一个声明为 auto 的变量
vector<int> v{ 1,3,3,7,9 };
auto f = [](int& x) { x += 1; };
for_each( v.begin(), v.end(),f );
如果一个 lambda 什么也不捕获,则我们可以将它赋值给一个指向正确类型函数的指针。
int (*p)(int) = [](int a) {return a; };
cout << (*p)(42) << endl; //输出 42
除了关于捕获的规则外,lambda 的大多数规则都是从函数和类借鉴而来。
然而,有两点需要注意:
auto f = [] {return 42; }; //lambda 最简形式:[]{表达式体};
lambda 表达式的调用方式与普通函数调用的方式相同。
auto max = [](int x, int y) { return x > y ? x : y; };
cout << max(10,30) << endl; //输出 30
当需要为一个 lambda 表达式定义返回类型时,必须使用尾置返回类型。
auto max = [](int x, double y) ->int
{
if (x > y)
return x;
else if (x < y)
return y;
else
return 0;
};
cout << max(10, 30) << endl; //输出 30
通过在捕获列表中列出局部变量的名字,就可以在 lambda 表达式中访问它们了。
与函数传递参数类似,通过值捕获传递的是局部变量的副本;不同的是,被捕获的变量的值是在 lambda 表达式创建时拷贝,而不是在调用时拷贝。
void fun(void)
{
int x =10, y = 20; //局部变量
auto max = [x,y](){ return x > y ? x : y; };
x = 30; // 对 x 的修改,不会改变 max() 返回的结果
cout << max() << endl; //输出 20
}
默认情况下,对于一个通过值捕获的变量的副本, lambda 不会改变其值。
如果希望能改变一个通过值捕获的变量的副本,就必须在参数列表后面加上关键字 mutable。
void fun(void)
{
int x = 1, y = 2; //局部变量
auto f = [x, y]() mutable { x += 10; y += 10; cout << x << ',' << y << endl; };
cout << x << ',' << y << endl; //调用前,x = 1,y = 2
f(); //调用中,x = 11,y = 12
cout << x << ',' << y << endl; //调用后,x = 1,y = 2
}
只有通过引用的捕获才允许修改调用环境中的变量。
void fun(void)
{
int x = 1, y = 2; //局部变量
auto f = [&x, &y]() { x += 10; y += 10; };
cout << x << ',' << y << endl; //调用前,x = 1,y = 2
f();
cout << x << ',' << y << endl; //调用后,x = 11,y = 12
}
lambda 引入符[]
的形式有很多种:
[]
空捕获列表。即,在 lambda 表达式内部无法使用其外层上下文中的任何局部名字。[&]
通过引用隐式捕获。所有局部名字都能使用,所有局部变量都通过引用访问。[=]
通过值隐式捕获。所有局部名字都能使用,所有局部名字都指向局部变量的副本。[捕获列表]
显示捕获。以&
为前缀的局部名字通过引用捕获,其它变量通过值捕获;捕获列表中,可以出现this
。[&,捕获列表]
对于名字没有出现在捕获列表中的局部变量,通过引用隐式捕获;捕获列表中,可以出现this
;列出的名字不能以&
为前缀;捕获列表中的变量名通过值的方式捕获。[=,捕获列表]
对于名字没有出现在捕获列表中的局部变量,通过值隐式捕获;捕获列表中,不允许包含this
;列出的名字必须以&
为前缀;捕获列表中的变量名通过引用的方式捕获。在捕获列表中,以&
为前缀的局部名字总是通过引用捕获;相反地,不以&
为前缀的局部名字总是通过值捕获。