C++ lambda表达式

一、 lambda表达式

C++11:也是一种可调用对象。定义了一个匿名函数,并且可以捕获一定范围内的变量。

1. 格式

[捕捉列表] (参数) mutable -> 返回值类型 {函数体};

注意:
1. 返回类型后置(必须的,语法规定);
2. 返回类型可以省略(但是编译器不一定能推断出返回类型,此时显示指定即可);
3. 参数列表可以有默认值。没有参数的时候,参数列表可以省略,甚至括号()都可以省略。
4. 捕获列表[]和函数体不能省略,必须时刻包含。
5. lambda调用方法和普通函数相同,不返回任何类型就是void。
6. 函数体末尾的分号不能省略。

	auto f = [](int a = 10)->int {
		return a + 1;
	};
	auto f1 = [] {return 1; };
	auto f2 = []() {return 2; };

	cout << f(1) << endl;

2. 捕捉列表说明

[]是lambda的引出符,捕捉列表能够捕捉上下文中的变量,来供lambda函数使用:

捕捉列表说明
[ ]不捕获任何变量,但不包括静态局部变量。lambda可以直接使用静态局部变量
[var]如果多个变量名,则彼此之间用,分割,按值捕获变量名代表的变量var,其他的变量则不捕获
[=]捕获外部作用域中所有变量,并作为值(副本)在函数体内使用,但不许给它赋值
[&var]按引用捕获变量名代表的变量,同时不捕获其他变量
[&]捕获外部作用域中所有变量,并作为引用在函数体内使用
[this]表示值传递方式捕捉当前的this指针,让lambda表达式有和当前类成员函数同样的访问权限
[=,&a]按值捕获所有外部变量,但按引用捕获&中所指的变量,=必须在开头位置,表示默认以值捕获
[&,var]按引用捕获所有外部变量,但按值的方式类捕获变量名所代表的变量

注意:
捉列表不允许变量重复传递,如:[=,a]、[&,&this],会引起编译时期的错误

class Test
{
public:
	void Func(int x, int y)
	{
		auto f = [this] {
			return m_a;
		};
		cout << f() << endl;
	}

private:
	int m_a = 10;
};
int main(int argc, const char* argv[]) 
{
	//1.[]不捕获任何变量
	int j = 5;
	//auto f = [] {return j; };	//编译报错
	
	//2. 静态局部变量,可以直接使用
	static int i = 9;
	auto f1 = [] {return i; };

	//3. [&]捕获外部作用域中所有变量,并作为引用在函数体内使用
	int m = 9;
	auto f2 = [&] {
		m = 15;
		return m;
	};
	cout << f2() << endl;	//15
	cout << m << endl;	//15
	
	//4. [=]捕获外部作用域中所有变量,并作为值(副本)在函数体内使用,但不许给它赋值
	int k = 1;
	auto f3 = [=] {
		//k = 15;	//非法,不可以给它赋值,因为是以值的方式捕获
	};
	
	//5. [this]捕获当前类中this指针,让lambda表达式有和当前类成员函数同样的访问权限。
	//如果[]中已经使用了 & 或 =,那么默认已经使用了this,可以使用当前类的成员变量和成员函数。
	Test test;
	test.Func(10, 12);

	//6. [变量名]如果多个变量名,则彼此之间用,分割,按值捕获变量名代表的变量,其他的变量则不捕获
	//	[&变量名]按引用捕获变量名代表的变量,同时不捕获其他变量
	int a = 10, b = 5;
	auto f4 = [a, b] {	//[&a, &b]
		cout << a <<" " <<  b << endl;
	};
	f4();

	//7. [=,&变量名] 按值捕获所有外部变量,但按引用捕获&中所指的变量,
	//=必须在开头位置,表示默认以值捕获(隐式捕获方式),后续其他的都是显示捕获
	auto f5 = [=, &a, &b] {
		cout << k << " " << a << " " << b << endl;
	};
	
	//8. [&, 变量名] 和7相反作用
	auto f6 = [&, a, b] {
		cout << k << " " << a << " " << b << endl;
	};

	return 0;
}

3. lambda表达式延迟调用易出错分析

	int x = 5;
	auto f = [=] {	//当遇到auto这一行,也就是捕获这个时刻,x的值就已经被复制到这个f中了
		return x;
	};
	x = 10;

	cout << x << endl;	//10
	cout << f() << endl;	//我们认为是10,但实际是5,该成&,值会被改成10

4. lambda表达式中的mutable(易变得)

	int x = 5;
	auto f = [=]() mutable {	//注意要加mutable,则()参数列表之外的这个圆括号不能省略
		x = 6;	//加mutable就可以修改x的值,理论上以值的方式捕获是不能被修改
		return x;
	};

5. lambda表达式的类型及存储

C++11中,lambda表达式的类型被称呼为 “闭包类型”(函数中的函数)。可以直接调用(可调用对象)。可以认为他是一个带有operator()的类类型对象,也就是仿函数。

所以我们可以用std::function和std::bind来保存和调用lambda表达式。每个lambda都会触发编译器生成一个独一无二的类对象。

	std::function<int(int)> f = [](int a) {return a; };
	cout << f(15) << endl;	//15

	std::function<int(int)> f1 = std::bind(	//bind第一个参数是函数指针,第二个参数才是真正的函数参数
		[](int a) {
			return a;
		},
		16
	);
	cout << f1(10) << endl;	//16

	//不捕获任何变量的lambda,也就是捕获列表为空,可以转换成一个普通的函数指针
	using F = int (*)(int);		//定义一个函数指针类型
	F f2 = [](int a) {return a; };
	cout << f(10) << endl;	//10

6. lambda表达式和for_each配合

for_each简介:

for_each是一个函数模板,使用时要包含头文件

#include <algorithm>
void Func(int a)
{
	cout << a << endl;
}

int main(int argc, const char* argv[]) 
{
	vector<int> vec{ 10,12,13,14,10,25 };
	for_each(vec.begin(), vec.end(), Func);	//传统for_each用法

	int sum = 0;
	for_each(vec.begin(), vec.end(), [&sum](int a) {
		sum += a;
		cout << a << endl;
		});

	cout << "sum= "<< sum << endl;

	return 0;
}

7. lambda表达式和find_if配合

find_if简介:

find_if是一个函数模板,使用时要包含头文件

#include <algorithm>
int main(int argc, const char* argv[]) 
{
	vector<int> vec{ 10,12,13,14,10,25 };
	auto iter = find_if(vec.begin(), vec.end(), [](int a) {		//返回的是一个迭代器
		cout << a << endl;
		return false;	//只要返回false,find_if会一直遍历,返回true就停止遍历
		});

	return 0;
}

二、捕获列表陷阱分析

1. 捕获列表的&

作用:捕获外部作用域中所有变量,并作为引用在lambda表达式中使用。

std::vector<std::function<bool(int)>> vec;	//全局变量,每个元素都是一个function
void Func()
{
	srand((unsigned)time(nullptr));
	int a = rand() % 6;	//产生一个0-5之间的随机数

	vec.push_back(
		//[&](int c)
		//[&](auto c)
		[=](int c)
		{
		if (c % a == 0)
		{
			return true;
		}
		return false;
		});
}

int main(int argc, const char* argv[]) 
{
	Func();
	cout << vec[0](10) << endl;	//非法调用,会出现不可预料的问题
	
	return 0;
}

2. 形参列表可以使用auto

C++14允许在lambda表达式的形参列表中使用auto。

引用捕获超出返回的情形也叫 “引用悬空”。采用按值捕获可解决这个问题。

3. 成员变量捕获问题

vector<std::function<bool(int)>> vec;
class Test
{
public:
	int m_a = 8;
	void Func() {
		auto tempv = m_a;	//解决办法,定义一个零时变量接住成员变量的,以值传递副本
		vec.push_back(	//不能[=]值传递
			[tempv](auto v) {	//按值捕获,等于有this
				cout << tempv << endl;
				if (v % tempv == 0)
				{
					return true;
				}
				return false;
			}
		);
	}
};
int main(int argc, const char* argv[]) 
{
	Test* p = new Test();
	p->Func();
	cout << vec[0](10) << endl;	//8,0
	delete p;	//结论:lambda表达式的执行正确与否,取决于p对象是否存在,只有p存在,这个lambda表达式执行才正确
	cout << vec[0](10) << endl;	//随机值,0
	//解决办法,参考 Func()函数 或参考 下面 广义lambda捕获
	return 0;
}

4. 广义lambda捕获

vector<std::function<bool(int)>> vec;
class Test
{
public:
	int m_a = 8;
	//void Func() {
	//	auto tempv = m_a;
	//	vec.push_back(
	//		[tempv](auto v) {	//按值捕获,等于有this
	//			cout << tempv << endl;
	//			if (v % tempv == 0)
	//			{
	//				return true;
	//			}
	//			return false;
	//		}
	//	);
	//}

	void Func() {
		vec.push_back(
			[abc = m_a](auto v) {	//将 m_a的值赋值到闭包里面来
				cout << abc << endl;
				if (v % abc == 0)
				{
					return true;
				}
				return false;
			}
		);
	}
};
int main(int argc, const char* argv[]) 
{
	Test* p = new Test();
	p->Func();
	delete p;	//结论:lambda表达式的执行正确与否,取决于p对象是否存在,只有p存在,这个lambda表达式执行才正确
	cout << vec[0](10) << endl;	//8,0
	
	return 0;
}

5. 静态局部变量

静态局部变量时不能被捕获的,但是可以在lambda中直接使用。静态局部变量时保存在静态存储区,它的有效期是一直到程序结束。

但是这种对static 变量使用有点类似于按 引用 捕获这种效果。

vector<std::function<bool(int)>> vec;
void Func()
{
	srand((unsigned)time(nullptr));
	//产生0-5之间的随机数
	static int temp = 4;	//静态局部变量不需要被捕获,也捕获不到
	vec.push_back([](auto v) {
		cout << temp << endl;
		if (v % temp == 0)
		{
			return true;
		}
		return false;
		});
	temp++;
}

int main(int argc, const char* argv[]) 
{
	Func();
	cout << vec[0](10) << endl;	//5,1
	
	return 0;
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值