C++ lambda 表达式
lambda 表达式
lambda 表达式有时也称为 lambda 函数,或者直接简称为 lambda ;它是定义和使用匿名函数对象的一种简便的方式。
一条 lambda 表达式包含以下组成要件:
- 一个可能为空的捕获列表,指明定义环境中的哪些名字能被用在 lambda 表达式内,以及这些名字的访问方式是拷贝还是引用;捕获列表位于
[]
内。 - 一个可选的参数列表,指明 lambda 表达式所需的参数;参数列表位于
()
内。 - 一个可选的 mutable 修饰符,指明该 lambda 表达式可能会修改它自身的状态(即,改变通过值捕获的变量的副本)。
- 一个可选的 noexcept 修饰符。
- 一个可选的
->
形式的返回类型声明。 - 一个表达式体,指明要执行的代码,表达式体位于
{}
内。
即,一个 lambda 表达式具有如下形式:
[捕获列表](参数列表)->返回值{表达式体};
在 lambda 表达式的概念中,传参、返回结果以及定义表达式体等环节都与函数的相应概念是一致的。
区别在于,函数没有提供局部变量“捕获”的功能;这意味着 lambda 表达式可以作为局部函数使用,而普通函数不能。
lambda 的类型
lambda 表达式是一种局部类类型,它含有一个构造函数以及一个 const 成员函数 operator()()。
lambda 表达式不仅可以作函数实参,还能用于初始化一个声明为 auto 或者std::function<R(AL)>
的变量。其中,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 的大多数规则都是从函数和类借鉴而来。
然而,有两点需要注意:
- 如果一个 lambda 表达式不接受任何参数,则其参数列表可被忽略。
- lambda 表达式的返回类型能由 lambda 表达式本事推断得到。
- 如果 lambda 的主体部分不包含 return 语句,则该 lambda 的类型是 void。
- 如果 lambda 的主体部分只包含一个 return 语句,则该 lambda 的类型是该 return 表达式的类型。
- 其它情况下,必须显示地提供一个返回类型。
可以忽略参数列表和返回类型,但是必须永远包含捕获列表和函数体。
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
;列出的名字必须以&
为前缀;捕获列表中的变量名通过引用的方式捕获。
在捕获列表中,以&
为前缀的局部名字总是通过引用捕获;相反地,不以&
为前缀的局部名字总是通过值捕获。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!