Lambda表达式(匿名函数)基础篇

1. 为什么需要Lamnda表达式?

1.解决回调函数的局限性;
了解回调函数请戳下面;

回调函数

void stable_sort(vector<string>::iterator iterBegin, vector<string>::iterator iterEnd, 
bool (*isShorter)(const string &, const string &))//C++ Primer里面举了个例子就是排序算法声明
//为了使排序算法适应不同类型的数据,并且能够按各种要求进行排序,机智的人类把排序算法做成了一个模版(在标准模版库STL里)
//,并且把判断两个数据之间的“大小”(也可以是“字节数”,或者其他某种可以比较的属性)
//而把它的具体实现就交给了使用排序算法的人
//

bool myIsShorter(const string &s1, const string &s2)
{    
     return s1.size()<s2.size();
}
stable_sort(words.begin(),words.end(),myIsShorter); //调用
//这里局限1在于传入的实参类型不能改变,定义是什么类型传入就必须是什么类型 
//局限2在于参数的参数个数已经被限制
//这里即可以引入Lambda
stable_sort(words.begin(),words.end(),[](){}); //具体如何实现,读者自行思考

2.Lambda表达式语法形式

在这里插入图片描述

  1. capture 子句(也称为lambda 引导C++ 规范中。)
  2. 参数列表可有可无. (也称为lambda 声明符)
  3. 可变规范可有可无.
  4. 异常规范可有可无.
  5. 尾随-返回类型可有可无.
  6. lambda 体
[函数对象参数] (操作符重载函数参数) mutable 或 exception 声明 -> 返回值类型 {函数体}
[](){} //定义
[](){}() //调用

3.Lambda表达式语法分析

[函数对象参数] 标识一个Lambda表达式的开始,必须存在,不能省略。
[函数对象参数] 只能使用那些到定义 Lambda 为止时 Lambda 所在作用范围内可见的局部变量(包括 Lambda 所在类
的 this)

/*假设有这么一个函数*/
	int a = 0;
	int b = 0;
	func(int param1, int param2, [=](){
	//这里只能可见 a,b  但是c看不到
	//因为函数对象参数只能使用那些到定义 Lambda 为止时 Lambda 所在作用范围内可见的局部变量
	});
	int c = 0;

// 1. []空。没有任何函数对象参数。
// 2. [=]=函数体内可以使用 Lambda 所在范围内所有可见的局部变量(包括 Lambda 所在类的 this),
//    并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
// 3. [&]&函数体内可以使用 Lambda 所在范围内所有可见的局部变量(包括 Lambda 所在类的 this),
//    并且是引用传递方式(相当于是编译器自动为我们按引用传递了所有局部变量)。
// 4. this 函数体内可以使用 Lambda 所在类中的成员变量。
// 5. [a] 将 a 按值进行传递。按值进行传递时,函数体内不能修改传递进来的 a 的拷贝,因为默认情况下函数是 //const 的,要修改传递进来的拷贝,可以添加 mutable 修饰符。
// 6. [&a] 将 a 按引用进行传递。
// 7. [a,&b] 将 a 按值传递,b 按引用进行传递。
// 8. [=,&a,&a] 除 a 和 b 按引用进行传递外,其他参数都按值进行传递。
// 9. [&,a,b] 除 a 和 b 按值进行传递外,其他参数都按引用进行传递。
(操作符重载函数参数)
//标识重载的 () 操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如: (a, b))和按引用 (如: (&a, &b)) 两种方式进行传递。
mutable 或 exception 声明
//这部分可以省略。按值传递函数对象参数时,加上 mutable 修饰符后,可以修改传递进来的拷贝(注意是能修改拷贝,而不是值本身)。exception 声明用于指定函数抛出的异常,如抛出整数类型的异常,可以使用 throw(int)。
-> 返回值类型
//标识函数返回值的类型,当返回值为 void,或者函数体中只有一处 return 的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
{函数体}
//标识函数的实现,这部分不能省略,但函数体可以为空。

3.针对基础语法的简单实例

[] (int x, int y) { return x + y; } // 隐式返回类型
[] (int& x) { ++x;  } // 没有 return 语句 -> Lambda 函数的返回类型是 'void'
[] () { ++global_x;  } // 没有参数,仅访问某个全局变量
[] { ++global_x; } // 与上一个相同,省略了 (操作符重载函数参数)
#include <iostream>
using namespace std;
int main()
{
int m = 0;
int n = 0;
[&, n] (int a) mutable { m = ++n + a; }(4);
cout << m << endl << n << endl;
}//输出结果为5 0
[&, n] //这里除了n是值传递,其余都是引用传递
(int a) //外部传入参数
mutable //按值传递函数对象参数时,加上 mutable 修饰符后,可以修改传递进来的拷贝 所以n的本身没有被修改
{ m = ++n + a; }//函数体
(4) //给参数传入实参,调用 
//因为m是引用传递,所以本身可以修改
std::vector<int> some_list;
int total = 0;
for (int i = 0; i < 5; ++i) some_list.push_back(i);
std::for_each(begin(some_list), end(some_list), [&total](int x)
{
    total += x;
}); 
#include <algorithm>
#include <cmath>
void abssort(float* x, unsigned n) {
std::sort(x, x + n,
// Lambda expression begins
[](float a, float b) {
return (std::abs(a) < std::abs(b));
} // end of lambda expression
);
}

4.总的来说,Lambda表达式经常用于以下场景

  1. 自定义比较函数的算法,如std::sort, std::nth_element, std::lower_bound等
  2. 标准库STL中使用,如std::find_if, std::remove_if, std::count_if等
  3. 能够为std::unique_ptr/std::shared_ptr快速创建自定义析构器
  4. 临时创建回调函数、接口适配函数供一次性调用

5注意事项

在使用 capture 子句时,建议你记住以下几点(尤其是使用采取多线程的 lambda 时):

  1. 引用捕获可用于修改外部变量,而值捕获却不能实现此操作。 (可变允许修改副本,而不是原始副本。)
  2. 引用捕获会反映外部变量的更新,而值捕获却不会反映。
  3. 引 用捕获引入生存期依赖项,而值捕获却没有生存期依赖项。 当 lambda 以异步方式运行时,这一点尤其重要。 如果在异步 lambda 中通过引用捕获本地变量,该本地变量将很可能在 lambda 运行时消失,从而导致运行时访问冲突。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值