【C++11】来感受lambda表达式的魅力~

news2024/11/24 3:51:16

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:C++航路
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨


目录

  • 一、仿函数的使用
  • 二、lambda表达式的语法
  • 三、lambda表达式中捕捉列表的玩法
  • 四、lambda表达式的底层原理
  • 五、总结

一、仿函数的使用

假设需要对vector容器里的数据进行排序,那么可以通过算法库的 sort 函数进行排序,至于结果为升序还是降序,可以通过 仿函数( 类+operator()重载) 控制。

struct Great // > - 降序
{
    bool operator()(int x, int y)
    {
        return x > y;
    }
};

struct Less // < - 升序
{
    bool operator()(int x, int y)
    {
        return x < y;
    }
};

int main()
{
    vector<int> v{1, 3, 5, 9, 7, 0, 4, 2, 6, 8};

    // Less() - 仿函数的匿名对象
    sort(v.begin(), v.end(), Less()); // 升序
    cout << "升序:";
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;

    // Great() - 仿函数的匿名对象
    sort(v.begin(), v.end(), Great()); // 降序
    cout << "降序:";
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;

    return 0;
}

【程序结果】

在这里插入图片描述

这看起来仿函数用起来也非常的香啊~。但如果某些程序员非常不注重代码风格,是这样写代码的:

struct func1 // > - 降序
{
    bool operator()(int x, int y)
    {
        return x > y;
    }
};

struct func2 // < - 升序
{
    bool operator()(int x, int y)
    {
        return x < y;
    }
};

sort(v.begin(), v.end(), func1()); 
sort(v.begin(), v.end(), func2()); 

这样写是不是非常的恶心,这样命名风格就算了,旁边还不给个注释,这不是欠揍嘛hh;而且万一仿函数在别的文件里,那么我们还要去找。

因此C++11支持了一个新语法:lambda 表达式。它可以快速构建局部的匿名函数对象,作为函数中的参数

接下来大家先见见猪跑,至于怎么使用lambda 表达式,后面会介绍

int main()
{
    vector<int> v{1, 3, 5, 9, 7, 0, 4, 2, 6, 8};

    sort(v.begin(), v.end(), [](int x, int y)->bool {return x < y; }); // 升序
    cout << "升序:";
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;

    // Great() - 仿函数的匿名对象
    sort(v.begin(), v.end(), [](int x, int y)->bool {return x > y; }); // 降序
    cout << "降序:";
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;

    return 0;
}

【程序结果】

在这里插入图片描述

由此可见,lambda表达式相对于传统的仿函数来说,更加灵活和简洁

二、lambda表达式的语法

书写格式:[ ]( ) mutable ->returntype { }

  • [ ] 捕捉列表。该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数。捕捉列表能够捕捉上下文中的变量供lambda函数使用

  • ( ) 参数列表。与普通函数的参数列表一个意思。如果没有参数,那么可以连通()一起省略。

  • mutable 关键字mutable可以取消捕捉列表中参数的常量属性。注意:使用该修饰符时,即使参数为空,参数列表的()不可省略。(但是基本不常用)

  • ->returntype 返回值类型。这里的返回值类型几乎可以不用写。

  • { } 函数体。和普通函数一样,当然支持多语句。但是不能函数体内不能使用局部的变量及函数,如果想使用,需要配合捕捉列表

这里再次声明:

  • lambda 表达式 构建出的是一个 局部的匿名函数对象,匿名函数对象也可以调用,不过要在创建后立马调用,因为匿名对象生命周期只有一行

  • 如果需要显式调用,可以使用 auto 推导 匿名函数对象 的类型,然后将创建出来的 匿名函数对象 赋给一个 有名函数对象

在这里插入图片描述

三、lambda表达式中捕捉列表的玩法

  • 捕获列表说明:捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用

    • [val]:表示值传递捕捉变量val
    • [&val]:表示引用传递捕捉变量val(修改变量)

    比如,使用lambda表达式交换局部变量ab

    在这里插入图片描述

    这是因为 捕捉列表 中的参数是一个传值捕捉,而捕捉列表 中的参数默认具有 常量属性,不能直接修改,但可以添加 mutable 关键字 取消常性
    在这里插入图片描述

    但输出结果发现两个值并没有修改。这里其实就类似于普通函数的值传递,局部变量的ab只是拷贝给捕捉列表参数,并没有建立联系,因此,这里的捕捉列表需要用引用传递的方式
    在这里插入图片描述

    • [&]表示引用传递捕捉所有在当前作用域中的所有变量

    需要注意的是:lambda函数体内不允许使用当前作用域中的变量,除非在捕捉列表声明,否则会报错。

    在这里插入图片描述

    这里有两个方法,要么在捕捉列表捕捉,要么将捕捉列表写成[&]表示引用传递捕捉所有在当前作用域中的所有变量

    在这里插入图片描述

    • [=]表示值传递捕捉所有在当前作用域中的所有变量。

    在这里插入图片描述

    注意:

    • 捕捉列表 的使用非常灵活,比如 [&, x] 表示只有x 使用 传值捕捉,其他变量使用 引用捕捉[=, &str] 表示 str 使用 引用捕捉,其他变量使用 传值捕捉

    • 捕捉列表不允许变量重复传递,否则就会导致编译错误。比如:[=, a]:捕捉a重复。

    • lambda表达式之间不能相互赋值。因为它们之间的类型是不一样的!至于为什么呢?后面讲底层就知道了~

    在这里插入图片描述

四、lambda表达式的底层原理

其实lambda表达式用起来非常香,这是因为它的底层是仿函数;就像范围for一样,看似是简短的几行代码,底层确实一个迭代器。

我们可以使用以下代码样例,通过查看反汇编:

struct func
{
    void operator()(int x, int y)
    {
        cout << "operator()(int x, int y)" << endl;
    }
};

int main()
{
    int a = 3;
    int b = 3;
    
    // 仿函数对象实例化
    func f;
    f(a, b);

    // lambda表达式
    auto e = [=](int x, int y) {
        cout << "[=](int x, int y)" << endl;
    };
    e(a, b);

    return 0;
a, b);

    return 0;
}

【反汇编】

在这里插入图片描述

实际在底层编译器对于lambda表达式的处理方式,完全就是按照仿函数的方式处理的,即:如
果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator(),而捕捉列表就相当于成员变量

并且这个类被命名为:lambda_uuid

uuid通用唯一标识码,可以生成一个重复率极低的辨识信息,避免类名冲突,这也意味着即便是两个功能完全一样的 lambda 表达式,也无法进行赋值,因为 lambda_uuid 肯定不一样

所以在编译器看来,lambda 表达式 本质上就是一个 仿函数

五、总结

当涉及到C++lambda表达式时,可以得出以下总结:

  1. 匿名函数: lambda表达式允许在需要函数对象的地方快速定义匿名函数,无需显式命名,可直接内联使用。

  2. 捕获外部变量: lambda表达式能够捕获其作用域内的变量,可以按值或按引用捕获,使得在算法和回调函数中处理外部变量更加方便。

  3. 简洁性: lambda表达式使代码更加紧凑和简洁,尤其在需要传递简单的函数对象时,可以省去冗余的代码。

  4. 可读性: 使用lambda表达式可以将算法和行为直接嵌入到使用它们的地方,使代码更具可读性和易于理解。

  5. 函数式编程: 引入lambda表达式后,C++增强了函数式编程的能力,使得处理一些函数式编程的场景更加便利。

需要注意的是,lambda表达式并不是完全取代传统的仿函数,而是在某些情况下更加方便和实用。例如,在需要长期复用、需要命名或需要在多个地方多次使用的情况下,传统的仿函数仍然有其价值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1512566.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何从笔记本电脑恢复已删除的照片

人们相信照片是回忆一生中最难忘事件的最佳媒介。人们在计算机上收集超过 5 GB 的照片是很常见的。然而&#xff0c;在笔记本电脑上保存照片并不安全&#xff0c;因为您可能会因以下原因有意或无意地删除笔记本电脑上的照片&#xff1a; 您的笔记本电脑存储空间几乎已满。您必…

nginx swrr负载均衡算法的二宗罪及其改进的思考

目录 1. swrr负载均衡算法的二宗罪1.1 第一宗罪: 共振引起系统崩溃1.2 第二宗罪: 吃CPU大户 2. 对swrr负载均衡算法的改进的思考2.1 “共振”问题的解决2.2 “吃CPU大户”问题的解决 1. swrr负载均衡算法的二宗罪 swrr是一种基于加权轮询的负载均衡算法。它根据服务器的权重来分…

第42期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

力扣L6--- 两数之和(java版)--2024年3月12日

1.题目 2.知识点 注1&#xff1a;在Java中&#xff0c;数组的长度不是通过调用一个方法获得的&#xff0c;而是通过一个属性直接获得的。因此&#xff0c;正确的语法是nums.length而不是nums.length()。 所以应该使用int m nums.length; 注2&#xff1a;return new int[]{i,…

保研复习数据结构记(4)--树(二叉树、线索树、哈夫曼树,并查集)

一.树的基本术语 1.树 什么是空树&#xff1f;结点数为0的树非空树的特性&#xff1f;有且仅有一个根结点&#xff0c;没有后继的结点称为“叶子结点”&#xff0c;有后继的结点称为“分支结点”&#xff0c;除了根结点外任何一个结点都有且仅有一个前驱&#xff0c;每个结点…

JS的对象

目录 对象&#xff1a;object 对象的创建&#xff1a; 利用对象字面量创建对象&#xff1a; 使用new来进行创建对象&#xff1a; 利用构造函数来创建对象&#xff1a; new的执行&#xff1a; 对象属性的遍历&#xff1a;for in ------ 相当于JAVA的工具类&#xff0c;直…

【NR 定位】3GPP NR Positioning 5G定位标准解读(十四)-DL-TDOA 定位

前言 3GPP NR Positioning 5G定位标准&#xff1a;3GPP TS 38.305 V18 3GPP 标准网址&#xff1a;Directory Listing /ftp/ 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;一&#xff09;-CSDN博客 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;…

git pull 报错: 在签出前,请清理存储库工作树

问题&#xff1a; 使用vscode 用git 拉取代码&#xff0c;提示&#xff1a;在签出前&#xff0c;请清理存储库工作树** 原因&#xff1a; git仓库上的代码和本地代码存在冲突了所以会报这个报错。 解决办法&#xff1a; ①git stash 先将本地修改存储起来 ②git pull 拉取远…

websocket 使用示例

websocket 使用示例 前言html中使用vue3中使用1、安装websocket依赖2、代码 vue2中使用1、安装websocket依赖2、代码 前言 即时通讯webSocket 的使用 html中使用 以下是一个简单的 HTML 页面示例&#xff0c;它连接到 WebSocket 服务器并包含一个文本框、一个发送按钮以及 …

案例--某站视频爬取

众所周知&#xff0c;某站的视频是&#xff1a; 由视频和音频分开的。 所以我们进行获取&#xff0c;需要分别获得它的音频和视频数据&#xff0c;然后进行音视频合并。 这么多年了&#xff0c;某站还是老样子&#xff0c;只要加个防盗链就能绕过。&#xff08;防止403&#xf…

第十四届蓝桥杯C++B组编程题题目以及题解

a.冶炼金属&#xff08;二分&#xff09; 思路&#xff1a; 设任意一条冶炼记录投入金属数量为a,产出金属为b. 对于每一条冶炼记录我们都可以得到 一个转换率V的范围&#xff1a; b<a/v<b1即a/b< v <a/(b1) 为什么是b1呢&#xff1f;因为既然能产出b个金属&#xf…

SpringCloud网关路由及实现

目录 1 前言 2 实现步骤 2.1 创建一个模块作为网关并引入相关依赖 2.3 设置启动类 2.4 配置路由 3 网关路由的补充内容 3.1 路由断言 3.2 路由过滤器 1 前言 前端请求不能直接访问微服务&#xff0c;而是要请求网关。原因及网关的作用如下&#xff1a; ①网关做安全控制…

16. UE5 RPG获取GE应用的回调,并根据Tag设置数据显示到窗口

在上一篇介绍了对标签如何在项目中设置&#xff0c;这一篇先讲解一下如何在GE里面使用GameplayTag标签。 之前我在第十一章节中 11. UE5 RPG使用GameplayEffect修改角色属性&#xff08;二&#xff09;介绍了一些GE的属性&#xff0c;在UE 5.3版本中&#xff0c;修改的配置方式…

Docker部署黑马商城项目笔记

部署后端 创建mysql目录如下&#xff0c;上传对应的文件 运行以下命令 docker run -d \--name mysql \-p 3306:3306 \-e TZAsia/Shanghai \-e MYSQL_ROOT_PASSWORD123 \-v ./mysql/data:/var/lib/mysql \-v ./mysql/conf:/etc/mysql/conf.d \-v ./mysql/init:/docker-entry…

LeetCode(力扣)算法题_1261_在受污染的二叉树中查找元素

今天是2024年3月12日&#xff0c;可能是因为今天是植树节的原因&#xff0c;今天的每日一题是二叉树&#x1f64f;&#x1f3fb; 在受污染的二叉树中查找元素 题目描述 给出一个满足下述规则的二叉树&#xff1a; root.val 0 如果 treeNode.val x 且 treeNode.left ! n…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的常见手势识别系统(深度学习模型+UI界面代码+训练数据集)

摘要&#xff1a;开发手势识别系统对于增强人机交互和智能家居控制领域的体验非常关键。本博客详尽阐述了通过深度学习技术构建手势识别系统的过程&#xff0c;并附上了全套实施代码。系统采用了先进的YOLOv8算法&#xff0c;并通过与YOLOv7、YOLOv6、YOLOv5的性能对比&#xf…

PTA- - -个位数统计(C语言)

Hello,好久没更新啦&#xff0c;今天给大家讲解一下PTA平台上面的“个位数统计”这道题吧~ 题目是要统计一个数字每个位上数字出现的次数。下面是一个解决方案的思路和相应的 C 语言代码&#xff1a; 思路&#xff1a; 初始化一个大小为10的数组&#xff0c;用于计数每个数字…

Kubernetes(k8s第四部分之servers)

1&#xff0c;为什么不使用round-robin DNS&#xff1f; 因为DNS有缓存&#xff0c;不会清理&#xff0c;无法负载均衡 ipvs代理模式&#xff0c;这种模式&#xff0c;kube-proxy会监视Kubernetes Service 对象和Endpoints&#xff0c;调用netlink接口以相应地创建ipvs规则并…

小迪安全39WEB 攻防-通用漏洞CSRFSSRF协议玩法内网探针漏洞利用

#知识点&#xff1a; 逻辑漏洞 1、CSRF-原理&危害&探针&利用等 2、SSRF-原理&危害&探针&利用等 3、CSRF&SSRF-黑盒下漏洞探针点 #详细点&#xff1a; CSRF 全称&#xff1a;Cross-site request forgery&#xff0c;即&#xff0c;跨站请求…

十四、软考-系统架构设计师笔记-云原生架构设计理论与实践

1、云原生架构背景 云原生架构定义 从技术的角度&#xff0c;云原生架构是基于云原生技术的一组架构原则和设计模式的集合&#xff0c;旨在将云应用中的非业务代码部分进行最大化的剥离&#xff0c;从而让云设施接管应用中原有的大量非功能特性(如弹性、韧性、安全、可观测性、…