总述

 C++11提供了对匿名函数的支持,称为Lambda函数(也叫Lambda表达式). 它是定义和使用匿名函数对象的一种简便的方式。匿名函数是我们需要用到的一个函数,但是又不想去费力命名一个函数的场景。我们无需为每个值或者每种类型)单独编写函数,更不必把值保存在让人厌倦的全局变量中 。 利用lambda表达式可以编写内嵌的匿名函数,用以替换独立函数或者函数对象,并且使代码更可读。

工作的时候这个是比较常用的,通过匿名函数进行函数内部变量的捕获,继而进行操作变量等。那么现在就由我来给大家分享一下,我对Lambda表达式的认知,仅作为一个基础的介绍哈,毕竟C++博大精深,吾辈还需要深究。

作者:良知犹存

转载授权以及围观:欢迎添加微信公众号:Conscience_Remains

它的结构

一条lambda表达式一般会有以下部分:

1.一个可能为空的捕获列表,指明定义环境中的那些名字能被用在lambda表达式内,以及这些名字的访问形式拷贝还是引用,捕获列表位于 [] 内。

2.一个可选的参数列表,指明lambda表达式所需的参数,参数列表位于 () 内。

3.一个可以选的mutable修饰符,指明该lambda表达式可能会修改它自身的状态(即,改变通过值捕获的变量的副本)

4.一个可选的 -> 形式的返回类型声明

5.一个表达式体,指明要执行的代码,表达式位于 {} 内。

[捕获列表](参数列表) mutable(可选) 异常属性 -> 返回类型 {
// 函数体
}

上面的语法规则除了 [捕获列表] 内的东西外,其他部分都很好理解,只是一般函数的函数名被略去, 返回值使用了一个 -> 的形式进行。

所谓捕获列表,其实可以理解为参数的一种类型,lambda 表达式内部函数体在默认情况下是不能够使用函数体外部的变量的, 这时候捕获列表可以起到传递外部数据的作用。

在lambda中,传参、返回结果以及定义表达式体和普通的函数都是一致的,区别就在于普通函数没有提供局部变量“捕获”功能,而局部捕获的功能,就意味着lambda可以做局部函数使用,而普通函数不能。

展示一个小例子证明lambda表达式的简洁性

Greater than 是一个函数对象,保存了要比较的值:

struct Greater_than {
int val;
Greater_than(lnt v) : val{v} { } ;
 bool operatorO(const pair<string.int>& r) { return r.second>val};
};

我们也可以使用 lambda 表达式 :

auto p =find_if(m.beginO, m.endO, 
[](const pair<string, int>& r) { return r.second>42; });
 

每当你定义一个lambda表达式后,编译器会自动生成一个匿名类(这个类当然重载了()运算符),我们称为闭包类型(closure type)。

基本的参数分析

C++11中的Lambda表达式捕获外部变量主要有以下形式:

  • []:默认不捕获任何变量;

  • [=]:默认以值捕获所有变量;

  • [&]:默认以引用捕获所有变量;

  • [x]:仅以值捕获x,其它变量不捕获;

  • [&x]:仅以引用捕获x,其它变量不捕获;

  • [=, &x]:默认以值捕获所有变量,但是x是例外,通过引用捕获;

  • [&, x]:默认以引用捕获所有变量,但是x是例外,通过值捕获;

  • [this]:通过引用捕获当前对象(其实是复制指针);

  • [*this]:通过传值方式捕获当前对象;

在上面的捕获方式中,注意最好不要使用[=]和[&]默认捕获所有变量。首先说默认引用捕获所有变量,你有很大可能会出现悬挂引用(Dangling references),因为引用捕获不会延长引用的变量的声明周期,例如一个形参传进来我们进行捕获并作为一个返回值执行。因为函数传参进来之后,本函数不会保存该变量,函数执行完就会自动释放,那么这个时候返回值就可能产生一个没有意义的结果。​​​​​​​

auto evt_set_status_x = [&](EventType x){  status[x] = true;/*通过引用捕获的变量 我们可以进行修改变量的数据*/};

    [&]是一个捕获列表(  capture l ist ), 它指明所用的局部名字(如 x) 将通过引用访问 。如果我们希望只"捕获 "x ,则可以写成 [&x] ;如果希望给生成的函数对象传递一个  的拷贝, 则写成[ x] 。什么也不捕获是[],捕获所有通过引用访问的局部名字是[&],捕获所有以值访问的局部名字是[=]  。     

并且lambda表达式也可以赋值给相对应的函数指针,这也使得你完全可以把lambda表达式看成对应函数类型的指针。

当我们需要访问它的局部变量的时候,我们需要特别定义捕获列表中的类型

下面是一个没有使用局部变量的lambda表达式,所以它的[]里面为空

​​​​​​​

void part(vector<int>& v){    
  sort(v.begin,v.end);//排列值
sort(v.begin,v.end,
    [](int x,int y){return abs(x) < abs(y);});//排列绝对值
}

下面是一个使用局部变量的lambda表达式,所以它的[]里面为空就会出错​​​​​​​

void part(vector<int>& v){    
  bool value = true;
sort(v.begin,v.end, [](int x,int y){return value ? x<y:abs(x) < abs(y);});
}

这时候就错误了,因为我们用到了value这个局部变量,而没有进行捕获列表的设置。

捕获使用分析

使用 lambda 虽然简单便捷,但也有可能显得晦涩难懂 。

值捕获

与参数传值类似,值捕获的前提是变量可以拷贝,不同之处则在于,被捕获的变量在 lambda 表达式被创建时拷贝, 而非调用时才拷贝:

​​​​​​​

#include <iostream>
void value_capture() {
int value = 1;
auto copy_value = [value] {
return value;
};
value = 100;
auto stored_value = copy_value();
    std::cout << "stored_value = " << stored_value << std::endl;
} int main(int argc,char ** argv)
{
value_capture();
}
// 这时, stored_value == 1, 而 value == 100.
// 因为 copy_value 在创建时就保存了一份 value 的拷贝

记得编译的时候加 -std=c++11

引用捕获

与引用传参类似,引用捕获保存的是引用,值会发生变化:

void reference_capture() {
int value = 1;
auto copy_value = [&value] {
return value;
};
value = 100;
auto stored_value = copy_value();
std::cout << "stored_value = " << stored_value << std::endl;
// 这时, stored_value == 100, value == 100.
// 因为 copy_value 保存的是引用
}

泛型lambda表达式

C++14开始,lambda表达式支持泛型:其参数可以使用自动推断类型的功能,而不需要显示地声明具体类型。这就如同函数模板一样,参数要使用类型自动推断功能,只需要将其类型指定为auto,类型推断规则与函数模板一样。就用我最早给出的那个例子好了。

auto evt_set_status_x = [&](EventType x){
status[x] = true;
};

  

​​​

这就是我分享的c++中的lambda表达式,以后有机会再往深入去分析一哈,其次如果大家有什么更好的思路,欢迎分享交流哈。

更多分享,扫码关注我

C++的匿名函数(lambda表达式)的更多相关文章

  1. 【Unity|C#】基础篇(9)——匿名函数 / Lambda表达式

    [学习资料] <C#图解教程>(第13章):https://www.cnblogs.com/moonache/p/7687551.html 电子书下载:https://pan.baidu. ...

  2. 匿名函数 lambda表达式(lambda expression)

    阅读g2log时,发现有两行代码居然看不懂. 1. auto bg_call =  [this, log_directory]() {return pimpl_->backgroundChang ...

  3. (28)C#委托,匿名函数,lambda表达式,事件

    一.委托 委托是一种用于封装命名和匿名方法的引用类型. 把方法当参数,传给另一个方法(这么说好理解,但实际上方法不能当参数,传入的是委托类型),委托是一种引用类型,委托里包含很多方法的引用 创建的方法 ...

  4. Qt中使用匿名函数lambda表达式

    一.为什么要使用匿名函数lamdba 首先,lambda表达式可以使代码变得简单,C++中,一个lambda表达式表示一个可调用的代码单元.如代码: #include <QCoreApplica ...

  5. Python匿名函数——lambda表达式

    如果要定义的函数很简单,一个return语句就能搞定,可以使用lambda表达式来定义, lambda表达式的语法如下: lambda parameters: expression lambda表达式 ...

  6. python做中学(八)匿名函数lambda的用法

    匿名函数,顾名思义即没有名称的函数,和def定义的函数的最大区别在于匿名函数创建后返回函数本身(即匿名函数不需要return来返回值),表达式本身结果就是返回值,而def创建后则赋值给一个变量名,在P ...

  7. 匿名函数 =匿名方法+ lambda 表达式

    匿名函数的定义和用途 匿名函数是一个"内联"语句或表达式,可在需要委托类型的任何地方使用. 可以使用匿名函数来初始化命名委托[无需取名字的委托],或传递命名委托(而不是命名委托类型 ...

  8. python--函数式编程 (高阶函数(map , reduce ,filter,sorted),匿名函数(lambda))

    1.1函数式编程 面向过程编程:我们通过把大段代码拆成函数,通过一层一层的函数,可以把复杂的任务分解成简单的任务,这种一步一步的分解可以称之为面向过程的程序设计.函数就是面向过程的程序设计的基本单元. ...

  9. 函数:lambda表达式 - 零基础入门学习Python021

    函数:lambda表达式 让编程改变世界 Change the world by program lambda表达式 Python允许使用lambda关键字来创建匿名函数.我们提到一个新的关键字:匿名 ...

  10. C# delegate event func action 匿名方法 lambda表达式

    delegate event action func 匿名方法 lambda表达式 delegate类似c++的函数指针,但是是类型安全的,可以指向多个函数, public delegate void ...

随机推荐

  1. day119:MoFang:宠物的状态改动&宠物粮道具的使用&宠物死亡处理

    目录 1.宠物的状态改动 2.宠物粮道具的使用 3.宠物死亡处理 1.宠物的状态改动 1.在setting表中为每个宠物配置生命周期时间 因为宠物有多个,每个宠物会有不同的初始生命的饥饿时间,所以我们 ...

  2. C++把数字排序

    C++把数字排序 描述 思路 代码 描述 如题,详细如下: 输入不超过1024个数字,以特殊数字结尾,如(-999),把数字从小到大排序. 思路 目前,我们有两种思路可以写: 1是 在输入的时候,排序 ...

  3. 【译】Async/Await(一)——多任务

    原文标题:Async/Await 原文链接:https://os.phil-opp.com/async-await/#multitasking 公众号: Rust 碎碎念 翻译 by: Praying ...

  4. Linux学习笔记 | 配置Samba

    Samba是在Linux和UNIX系统上实现SMB协议的一个免费软件,由服务器及客户端程序构成.SMB(Server Messages Block,信息服务块)是一种在局域网上共享文件和打印机的一种通 ...

  5. 关于maven多module的依赖问题

    之前的项目因为历史的原因,都是一个project里只包含了一个module,今年进入了新的项目组,出现了多个module,最近刚好也是在学<maven实战>因此想要将这个东西记录下来 工程 ...

  6. 【Problems】MySQL5.7 datetime 默认值设为‘0000-00-00 00:00:00'值出错

    记录 MySQL5.7 datetime 默认值设为'0000-00-00 00:00:00'值出错 我的MySQL版本 mysql --version 5.7.28 C:\Users\x1c> ...

  7. 【Linux】使用grep快速比较两个文件不同

    两个文件的比较,会有同学说使用diff,和vimdiff就可以快速比较,为什么还要使用grep呢? 有些时候,diff和vimdiff的时候环境不符合,这样的情况,就可以使用grep来解决这个问题. ...

  8. 【win10】win10下两个显示器不同桌面壁纸

    win10系统下,双屏显示为不同的桌面壁纸 操作: 1.鼠标右键点击个性化 2.点击背景选项 3.在图片上右键选择要添加为背景的图片 同理,将另一个屏幕壁纸设为监视器1 最后效果为两个分屏为不同桌面壁 ...

  9. 机器学习7-模型保存&无监督学习

    模型保存和加载 sklearn模型的保存和加载API from sklearn.externals import joblib 保存:joblib.dump(rf, 'test.pkl') 加载:es ...

  10. MVC和MTV框架模式

    1. MVC: MVC,全名是Model View Controller,是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model).视图(View)和控制器(Controller ...