C++:lambda表达式

前言:

将以前自己做的lambda表达式的笔记传到博客上来,一来供自己查阅,二来给有需要的人提供思路。



0x01)lambda表达式

我们可以向一个算法传递任何类型的可调用对象

对于一个对象或一个表达式,如果可以对其使用调用运算符,则称它为可调用的。比如e是一个可调用的表达式,则我们可以编写代码e(args),其中args是一个都好分的一个或多个参数的列表。

**可调用的对象:**函数、函数指针、重载了调用运算符的类以及lambda表达式。

一个lambda表达式表示一个可调用的代码单元,我们可以将其理解为一个未命名的内联函数

与任何函数类似,一个lambda表达式具有一个返回类型,一个参数列表和一个函数体。但与函数不同,lambda可能定义在函数内部

1、lambda表达式形式如下:[capture] (parameters) mutable ->return-type{statement}

1.[capture]:捕捉列表。捕捉列表总是出现在Lambda函数的开始处。实际上,[]是Lambda引出符。编译器根据该引出符判断接下来的代码是否是Lambda函数。捕捉列表能够捕捉上下文中的变量以供Lambda函数使用;

2.(parameters):参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号“()”一起省略;

3.mutable:mutable修饰符。默认情况下,Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空);

4.->return-type:返回类型。用追踪返回类型形式声明函数的返回类型。我们可以在不需要返回值的时候也可以连同符号”->”一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导(auto);

5.{statement}:函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量

与普通函数最大的区别是,除了可以使用参数以外,Lambda函数还可以通过捕获列表访问一些上下文中的数据。具体地,捕捉列表描述了上下文中哪些数据可以被Lambda使用,以及使用方式(以值传递的方式或引用传递的方式)。语法上,在“[]”包括起来的是捕捉列表,捕捉列表由多个捕捉项组成,并以逗号分隔。捕捉列表有以下几种形式:

1.[var]表示值传递方式捕捉变量var
2.[=]表示值传递方式捕捉所有父作用域的变量(包括this);
3.[&var]表示引用传递捕捉变量var
4.[&]表示引用传递方式捕捉所有父作用域的变量(包括this); 5.[this]表示值传递方式捕捉当前的this指针
6.[]表示一个空捕获列表,它不使用任何在lambda{}中的局部变量。 7.[=,&identifier_list]identifier中的变量采用引用方式捕获,但是其中不能包含this指针。

注意:这里的父作用域就是包含lambda表达式的整个作用域,即lambda表达式在这个{}作用域中,不是lambda表示式里的函数体{}。

在这里插入图片描述
在这里插入图片描述

举例如下:

#include <iostream>
using namespace std;

int main()
{
	int j = 10;
	auto by_val_lambda = [=]()mutable{return j + 1; };
	auto by_ref_lambda = [&]{return j + 1; };
	cout << "by_val_lambda:" <<by_val_lambda() << endl;
	cout << "by_ref_lambda:" << by_ref_lambda() << endl;

	++j;
	cout << "by_val_lambda:" << by_val_lambda() << endl;
	cout << "by_ref_lambda:" << by_ref_lambda() << endl;

	system("pause");
	return 0;
}

输入结果:11 11 11 12

对于by_val_lambda由于捕获列表是进行的值拷贝,所以被捕获变量的值在lambda创建时拷贝,而不是调用时拷贝。这样可解释by_val_lambda中的j的值创建时被拷贝为10,一旦拷贝完成,随后父作用域对j进行修改也不会影响到lambda内对应的j的值。

对于by_ref_lambda由于调用by_ref_lambda()的引用j绑定的是全局作用域的j,所以全局作用的j变化,调用它时j也会发生变化,它的变化也会影响到全局作用域中的j。

2、向lambda传递参数

与一个普通函数调用类型,调用一个lambda时需要用给定的实参来初始化lambda的形参

/*按长度排序,长度相同的单词维持字典序*/
stable_sort(words.begin(),words.end(),[](const string& a,const string& b){return a.size()<b.size();});

空捕获列表表明此lambda不使用lambda{}中的任何局部变量

当stable_sort需要比较两个元素时,它就会调用给定的这个lambda表达式。

3、使用捕获列表

虽然一个lambda可以出现在一个函数中,使用其局部变量,但是它只能使用那些明确指明的变量。一个lambda通过将局部变量包含在其捕获列表中来指出将会使用的这些变量。捕获列表指引lambda在其内部包含访问局部变量所需的信息。

[sz](const string& a){return a.size()>=sz;};

在这里插入图片描述

注:只有捕获了sz,函数体中才能使用sz这个局部变量。

4、调用find_if

/*获取第一个满足长度大于sz元素的迭代器。若找不到长度大于sz的元素则返回words.end()的拷贝*/
/*前提是:words按长度排序,长度相同的单词维持字典序*/
auto wc=find_if(words.begin(),words.end(),[sz](const string& a){return a.size()>=sz;});

5、for_each算法

/*打印长度大于等于给定值的单词,每个单词后面接一个空格*/
for_each(wc,words.end(),[](const string& s){cout<<s<<" ";});

我们只在lambda所在函数中定义的==(非static)变量==使用捕获列表。一个lambda也可以使用定义在当前函数之外的名字。

6、完整的biggies

void biggies(vector<string> &words,vector<string>::size_type sz)
{
    elimDups(words);//将words按字典排序,删除重复单词
    /*按长度排序,长度相同的单词维持字典序*/
    stable_wort(words.begin(),words.end(),[](const string& a,const string& b){return a.size()<b.size();});
    /*获取一个迭代器,指向第一个满足size()>sz的元素*/
    auto wc=find_if(words.begin(),words.end(),[sz](const string& a){return a.size()>=sz;});
    /*计算满足size>=sz的元素的数目*/
    auto count=words.end()-wc;
    /*打印长度大于等于给定值的单词,每个单词后面接一个空格*/
    for_each(wc,words.end(),[](const string &s){cout<<s<<" ";});
    cout<<endl;
}


0x02)lambda捕获和返回

定义一个lambda时,编译器生成一个与lambda对应的新的(未命名的)类类型

即向一个函数传递一个lambda时,同时定义了一个新类型和该类型的一个对象传递的参数就是此编译器生成的类类型的未命名对象。类似的,当使用auto定义一个用lambda初始化的变量时,也就是定义了一个从lambda生成的类型的对象。

1、值捕获

lambda表达式采用值捕获的前提是捕获的变量可以拷贝。与参数不同的是,被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝。

由于被捕获变量的值是在lambda创建时拷贝,因此随后对其修改不会影响到lambda内对应的值。

void test()
{
    size_t v1=42;//局部变量
    auto f=[v1]{return v1;};/*将v1拷贝到名为f的可调用对象*/
    v1=0auto j=f();//j=42,f保存了我们创建它时v1的拷贝
}

2、引用捕获

void test()
{
    size_t v1=42;//局部变量
    //对象f2包含v1的引用
    auto f2=[&v1]{return v1;};
    v1=0auto j=f2();//j=0,f保存了v1的引用,而非拷贝
}

一个以引用方式捕获的变量与其他任何类型的引用的行为类似。当我们在lambda函数体内使用此变量时,实际上使用的是引用所绑定的对象。本例中,当lambda返回v1时,它返回的是v1所指向的对象的值

当以引用方式捕获一个变量时,必须保证在lambda执行时变量是存在的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b54mUVbi-1641542697053)(C:\Users\小能\AppData\Roaming\Typora\typora-user-images\1563763176512.png)]

3、隐式捕获

为了指示编译器推断捕获列表,应在捕获列表中写一个&或=

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0VHWgyOw-1641542697056)(C:\Users\小能\AppData\Roaming\Typora\typora-user-images\1566048967462.png)]

4、可变lambda

由于默认情况下lambda是一个const函数,所以对于一个以值被拷贝的变量,需要在参数列表前面添加关键字matable来修改被捕获的变量的值。

5、指定lambda返回类型

默认情况下,如果一个lambda包含return之外的任何语句,则编译器假定lambda返回void,因此返回void的lambda不能有返回值。

当lambda体是单一的return语句时,返回一个条件表达式的结果,我们无需指定其返回类型,编译器可根据条件运算符的类型来推导。

当我们需要为一个lambda定义返回类型时,必须使用尾置返回类型

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值