C++之lambda表达式

1. 什么是Lambda表达式?

在介绍lambda表达式前先看下面这个例子:

int main() {
	[](){};
	return 0;
}

没错,括号三巨头(中括号、小括号、大括号)就是一个lambda表达式。这个lambda表达式没有函数名、没有参数、也没有函数体,但这三个括号对应了lambda表达式三个非常重要的组成部分。

C++中的lambda表达式可以看作是一个匿名函数,也可以当作一个内联函数来使用。内联函数的意义在这里不展开讨论,而lambda表达式作为匿名函数在很多地方是大有用处的。

此外,lambda表达式是一种可调用对象。C++中可调用对象还有函数、函数指针和重载了"()"运算符的类等。

2. Lambda表达式的结构

C++中的lambda表达式的结构如下所示:

[capture_list] (parameter_list) mutable exception -> return_type {function_body}

其中,加粗的两项为必选项,其他部分为可选项。各组成部分的具体意义为:
1. capture_list 捕获列表,列表中的局部变量可以被lambda表达式使用。
2. parameter_list 参数列表,相当于函数的参数列表。
3. mutable 用来说明捕获列表中的变量能否被修改。
4. exception 声明异常。
5. return_type 返回值类型。

(1)参数列表

C++中lambda表达式的参数列表和普通函数的参数列表类似,不同点如下:

1. lambda表达式的形参类型可以设置为auto(C++14开始支持),编译器会根据传入的值进行判断,而普通函数的形参必须给定参数类型。
2. lambda表达式的形参不能为默认参数。(这一点可能在C++11后不再限制)

int main() {
	auto lam = [](auto elem1 ,auto elem2) {return elem1 > elem2 ? elem1 : elem2; };
	cout << "max(2,3)=" << lam(2, 3) << endl;
	return 0;
}
max(2,3)=3

(2)返回值类型

大多数情况下lambda表达式的返回值可由编译器猜测得出,因此不需要我们指定lambda表达式的返回值类型。我们可以通过尾值返回类型的方式设置lambda表达式的返回类型。

int main() {
	auto lam = [](int elem) -> int {return -elem; };
	cout << lam(3) << endl;
	return 0;
}
 -3

(3)捕获列表

为了说明捕获列表的用途,先看下面这个例子:

int main() {
	int val = 10;
	auto lam = [val](int elem){return val-elem; };
	cout << "10 - 3 = " << lam(3) << endl;
	return 0;
}

lambda表达式通过将局部变量包含在其捕获列表中来指出其将会使用的局部变量。这里lambda表达式需要使用mian函数中的局部变量val,因此将val放入了捕获列表。
注意lambda表达式可以直接使用静态变量或者是外部变量;如果需要使用到局部非静态变量才需要进行捕获。

捕获的两种类型

1. 显式捕获:当需要使用到非静态局部变量的时候,需要显式的进行捕获,将该变量列入到捕获列表中。
2. 隐式捕获:对于静态变量和外部变量,系统可以根据我们的使用情况推断出需要捕获的变量,因此不需要显式的将其列入捕获列表中。

int main() {
	int val = 10;
	vector<int> vec = { 1, 2, 3, 4, 5 };
	for_each(vec.begin(), vec.end(), [=](int& elem) {elem+=val;});
	return 0;
}

注意隐式捕获需要指定捕获模式!关于捕获的两种模式下面会讨论到。

捕获的两种模式

捕获也有两种方式:值捕获引用捕获。下面两种形式分别表示值捕获和引用捕获。
1. [=(identifier)] 值捕获 ;
2. [&(identifier)] 引用捕获 ;

混合使用显式捕获和隐式捕获

当需要对非静态局部变量、静态变量、外部变量等同时进行捕获时,需要混合显式和隐式捕获,所有规则如下:

捕获列表捕获类型
[]空捕获
[identifier_list]显式值捕获
[&identifier_list]显式引用捕获
[=]隐式值捕获
[&]隐式引用捕获
[=, identifier_list]混合捕获,隐式值捕获和显式引用捕获
[&, identifier_list]混合捕获,隐式引用捕获和显式值捕获

其中最后两项为混合捕获,当隐式捕获为值捕获时,显式捕获只能为引用捕获;当隐式捕获为引用捕获时,显式捕获只能为值捕获。

(4)可变lambda

在介绍可变lambda之前先看一下什么是常量函数。C++中,常量成员函数无法修改成员变量的值。如下例:

class Func {
public:
	int func_num = 0;
	void modify(int val)const{ this->func_num = val;}
};

上述程序中modify成员函数用const进行修饰,它是一个常量成员函数,无法修改成员变量func_num的值。因此上程序会在编译时报错。

下面看这个lambda表达式的例子:

int main() {
	int val = 10;
	vector<int> vec = { 1, 2, 3, 4, 5 };
	for_each(vec.begin(), vec.end(), [=](int& elem){elem += (--val); });
	return 0;
}

这里采用了隐式值捕获,但由于lambda函数也是const函数,这里不能对val的值进行修改。因此该程序在编译时也会报错。

解决方式是mutable关键字

int main() {
	int val = 10;
	vector<int> vec = { 1, 2, 3, 4, 5 };
	for_each(vec.begin(), vec.end(), [=](int& elem)mutable{ elem += (--val); });
	for (auto i : vec) cout << i << " ";
	cout << "\nval = " << val;
	return 0;
}

这样就可以在lambda函数中修改val的值了。但注意由于这里是值传递,lambda函数修改的实际上是val的一个拷贝,也就是一个临时变量。
上程序的运行结果为:

10 10 10 10 10
val = 10

从结果可以看出,vec中的元素全部变为了10,且val的值仍旧为10。

3. Lambda表达式的用处

lambda表达式一个很重要的作用就是配合STL库中的各种算法,使得其结构更加简洁紧凑,功能也更加的强大。下面举几个简单的例子:
(1)配合for_each()

int main() {
	vector<int> vec = { 1, 2, 3, 4, 5 };
	for_each(vec.begin(), vec.end(), [](int& elem){ elem =elem*elem; });
	for (auto i : vec) cout << i << " ";
	return 0;
}
1 4 9 16 25

(2)自定义排序

struct Point {
public:
	int x, y;
	Point(int x_val, int y_val) :x(x_val), y(y_val) {};
};
int main() {
	Point p1(3, 2), p2(6, -2), p3(4, 4);
	vector<Point> vec = {p1, p2, p3};
	sort(vec.begin(), vec.end(), 
								[](Point elem1, Point elem2){
		return elem1.x < elem2.x; });
	print_vec(vec);
	return 0;
}
(3,2) (4,4) (6,-2)

(3) 计数

int main() {
	int num;
	vector<int> vec = { 3, 2, 4, 4, 1, 5, 8, 2, 4 };
	print_container(vec);
	num=count(vec.begin(), vec.end(), 4);//count
	cout << "N(element=4): " << num << endl;
	num = count_if(vec.begin(), vec.end(),
				   [](int elem) {return elem > 4; });//count_if
	cout << "N(element>4): " << num << endl;
	num = count_if(vec.begin(), vec.end(),
		           [](int elem) {return elem % 4 == 0; });//count_if
	cout << "N(element%4=0): " << num << endl;
	return 0;
}
Element: 3 2 4 4 1 5 8 2 4
N(element=4): 3
N(element>4): 2
N(element%4=0): 4
  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值