【C++ 学习 ㉛】- 详解 C++11 的 lambda 表达式

news2025/1/18 16:55:00

目录

一、语法格式

二、函数对象和 lambda 表达式


 


一、语法格式

定义一个 lambda 表达式(lambda 函数)的语法格式如下:

[capture-list] (parameters) mutable noexcept/throw() -> return-type { statements };
即
[捕获列表] (参数列表) 可变规则 异常说明 -> 返回类型 { 函数体 };

其中各部分的含义分别为:

  1. [捕获列表]:[] 是 lambda 引出符,编译器根据该引出符判断接下来的代码是否为 lambda 函数,所以其不能被省略。捕获列表能够捕获外部变量(即和当前 lambda 函数位于同一作用域内的所有局部变量)以供 lambda 函数使用

    捕获列表可由多个捕获项组成,并以逗号分割。捕获列表有以下几种形式

    [捕获列表]说明
    []不捕获任何外部变量
    [=]以值传递的方式捕获所有外部变量(包括 this)
    [&]以引用传递的方式捕获所有外部变量(包括 this)
    [val1, val2, ...]以值传递的方式捕获 val1、val2 等指定的外部变量
    [&val1, &val2, ...]以引用传递的方式捕获 val1、val2 等指定的外部变量
    [=, &val1, ...]以引用传递的方式捕获 val1 等指定的外部变量,以值传递的方式捕获其他所有外部变量
    [&, val1, ...]以值传递的方式捕获 val1 等指定的外部变量,以引用传递的方式捕获其他所有外部变量
    [this]以值传递的方式捕获当前的 this 指针

    注意:捕获列表不允许外部变量重复传递,例如 [=, val1]、[&、&val1]

  2. (参数列表):和普通函数不同的是,如果不需要传递参数,可以连同 () 一起省略

  3. 可变规则:mutable 关键字可以省略,如果要使用,则之前的 () 将不能省略(参数个数可以为 0)。默认情况下,以值传递方式捕获的外部变量,不允许在 lambda 函数内部修改它们的值(可以理解为这部分变量都是 const 变量),如果想修改它们,就必须使用 mutable 关键字

    注意:修改以值传递方式捕获的外部变量,实则修改的是外部变量的拷贝,而不是真正的外部变量

  4. 异常说明:可以省略,如果要使用,则之前的 () 将不能省略(参数个数可以为 0)。默认情况下,lambda 函数的函数体中可以抛出任何类型的异常,标注 noexcept 关键字,则表示函数体内不会抛出任何异常;使用 throw() 则可以指定 lambda 函数内部可以抛出的异常类型

  5. -> 返回类型:返回类型为 void,可以省略;返回类型明确的情况下,也可以省略,由编译器对返回类型进行推导

  6. { 函数体 }:函数体内部除了可以使用指定传递进来的参数,还可以使用所有捕获的外部变量以及全局范围内的所有全局变量

示例一

#include <iostream>
using namespace std;
​
class A
{
public:
    A(int x = 0) : _i(x) { }
​
    void sayHello() const { cout << "Hello" << endl; }
​
    void test()
    {
        // 以值传递的方式捕获当前的 this 指针
        auto func = [this] {
            this->_i = 10;
            cout << this->_i << endl;
            this->sayHello();
            // 函数体中 this 可以省略
        };
        func();
    }
private:
    int _i;
};
​
int main()
{
    // 最简单的 lambda 表达式,该表达式没有任何意义
    [] {};
​
    A a;
    a.test();
    // 10
    // Hello
    return 0;
}

示例二

#include <string>
#include <vector>
#include <algorithm>
using namespace std;
​
// 商品类
struct Goods
{
    string _name;  // 名字
    double _price;  // 价格
    int _eval;  // 评价
​
    Goods(const char* str, double price, int eval)
        : _name(str), _price(price), _eval(eval)
    { }
};
​
struct CmpByPriceLess
{
    bool operator()(const Goods& lhs, const Goods& rhs)
    {
        return lhs._price < rhs._price;
    }
};
​
struct CmpByPriceGreater
{
    bool operator()(const Goods& lhs, const Goods& rhs)
    {
        return lhs._price > rhs._price;
    }
};
​
int main()
{
    vector<Goods> v = {
        { "苹果", 2.1, 5 },
        { "香蕉", 3.0, 4 },
        { "橙子", 2.2, 3 },
        { "菠萝", 1.5, 4}
    };
​
    // 按价格排序方法一:
    sort(v.begin(), v.end(), CmpByPriceLess());  // 升序
    sort(v.begin(), v.end(), CmpByPriceGreater());  // 降序
​
    // 按价格排序方法二:
    sort(v.begin(), v.end(), [](const Goods& lhs, const Goods& rhs) {
        return lhs._price < rhs._price; });  // 升序
    sort(v.begin(), v.end(), [](const Goods& lhs, const Goods& rhs) {
        return lhs._price > rhs._price; });  // 降序
    return 0;
}


二、函数对象和 lambda 表达式

// 利率类
class Rate
{
public:
    Rate(double rate) : _rate(rate) { }
​
    double operator()(double money, int year)
    {
        return money * _rate * year;  // 利息 = 本金 * 利率 * 存期
    }
private:
    double _rate;
};
​
int main()
{
    double rate = 0.49;
    // 函数对象
    Rate r1(rate);  
    r1(1000, 2);
​
    // lambda 表达式
    auto r2 = [rate](double money, int year) {
        return money * rate * year;
    }; 
    r2(1000, 2);
    return 0;
}

所以实际上在底层中,编译器对一个 lambda 表达式的处理方式,完全就是按照函数对象的方式来处理的,即如果定义了一个 lambda 表达式,编译器会自动生成一个类,该类中重载了 operator()

注意:lambda 表达式即使看起来是一样,但它们的类型实际上是不相同的

#include <iostream>
using namespace std;
​
int main()
{
    auto add1 = [](int x, int y) { return x + y; };
    auto add2 = [](int x, int y) { return x + y; };
    cout << typeid(add1).name() << endl;
    // class <lambda_2af21e002ff22901d6b0fa11901f235e>
    cout << typeid(add2).name() << endl;
    // class <lambda_751ae2273733b2c2d261a415ac8d186d>
    return 0;
}

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

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

相关文章

register_parameter和register_buffer 详解

在参考yolo系列代码或其他开源代码&#xff0c;经常看到register_buffer和 register_parameter的使用&#xff0c;接下来将详细对他们进行介绍。 1. 前沿 在搭建网络时&#xff0c;我们 自定义的参数&#xff0c;往往不会保存到模型权重文件中&#xff0c;或者成为模型可学习…

【多线程】并发问题

public class BuyTicket implements Runnable{private int ticketNums10;Overridepublic void run() {for(int i1;i<ticketNums;i){if(ticketNums<0){break;}System.out.println(Thread.currentThread().getName() "抢到了第" i "张票");ticketNu…

数字处理-第10届蓝桥杯省赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第3讲。 数字处理&#xff…

高性能网络编程 - 关于单台服务器并发TCP连接数理论值的讨论

文章目录 概述操作系统的限制因素文件句柄限制1. 进程限制2. 全局限制 端口号范围限制 概述 单台服务器可以支持的并发TCP连接数取决于多个因素&#xff0c;包括硬件性能、操作系统限制、网络带宽和应用程序设计。以下是一些影响并发TCP连接数的因素&#xff1a; 服务器硬件性…

软件设计模式原则(二)开闭原则

继续讲解第二个重要的设计模式原则——开闭原则~ 一.定义 开闭原则&#xff0c;在面向对象编程领域中&#xff0c;规定“软件中的对象&#xff08;类&#xff0c;模块&#xff0c;函数等等&#xff09;应该对于扩展是开放的&#xff0c;但是对于修改是封闭的”&#xff0c;这意…

Golang源码分析之golang/sync之singleflight

1.1. 项目介绍 golang/sync库拓展了官方自带的sync库&#xff0c;提供了errgroup、semaphore、singleflight及syncmap四个包&#xff0c;本次分析singlefliht的源代码。 singlefliht用于解决单机协程并发调用下的重复调用问题&#xff0c;常与缓存一起使用&#xff0c;避免缓存…

Capto2024专为Mac电脑设计的屏幕录制和视频编辑软件

不得不说视频编辑功能&#xff1a;Capto提供了多种视频编辑功能&#xff0c;例如剪辑、旋转、裁剪、调整音频和视频的音量、加入水印、添加注释等&#xff0c;你能够使用Capto编辑你的视频&#xff0c;使之更加专业和生动。有目共睹的是录制完成后&#xff0c;你能够使用Capto提…

PowerShell实战:文件操作相关命令笔记

目录 1、New-Item 创建新项命令 2、Remove-Item 删除项命令 3、Rename-Item 项重命名 1、New-Item 创建新项命令 cmdlet New-Item 将创建新项并设置其值。 可创建的项类型取决于项的位置。 例如&#xff0c;在文件系统 New-Item 中创建文件和文件夹。 在注册表中&#xff0c; N…

叶片卷曲

叶片卷曲 上卷/内卷白粉病强烈阳光&温度太高虫害&#xff08;蓟马&#xff09; 下卷 叶片卷曲的原因有很多&#xff0c;很多情况无法从外表分辨&#xff0c;并且有可能多种原因混杂&#xff0c;扰乱判断 上卷/内卷 白粉病 当植株感染白粉病时&#xff0c;白粉病菌孢子附…

c语言进阶部分详解(《高质量C-C++编程》经典例题讲解及柔性数组)

上篇文章我介绍了介绍动态内存管理 的相关内容&#xff1a;c语言进阶部分详解&#xff08;详细解析动态内存管理&#xff09;-CSDN博客 各种源码大家可以去我的github主页进行查找&#xff1a;唔姆/比特学习过程2 (gitee.com) 今天便接“上回书所言”&#xff0c;来介绍《高质…

CANoe新建XML自动化Test Modules

文章目录 1.打开Test Modules2.新建Environment3.新建XML Test Modules4.新建.can文件5.打开XML Test Modules6.新建xml脚本并保存7.编译8.在.can文件写个测试用例9.修改报告格式为HTML10.运行查看报告后面介绍的文章会重复用到这部分,这里单独介绍下,后面不做重复介绍。 1.…

Envoy XDS协议学习

Envoy xds学习 资料地址 envoy官网资料连接 接口说明 xds分为增量接口和全量接口SotW&#xff1a;state of the world 即全量的数据Incremental&#xff1a; 增量的数据 具体接口 Listener: Listener Discovery Service (LDS) SotW: ListenerDiscoveryService.StreamList…

一文搞懂设计模式之工厂模式

大家好&#xff0c;我是晴天&#xff0c;本周将同大家一起学习设计模式系列的第二篇文章——工厂模式&#xff0c;我们将依次学习简单工厂模式&#xff0c;工厂方法模式和抽象工厂模式。拿好纸和笔&#xff0c;我们现在开始啦~ 前言 我们在进行软件开发的时候&#xff0c;虽然…

vector类模拟实现(c++)(学习笔记)

vector 构造函数析构函数[]push_backsize()capacity()reserve()push_back() 迭代器实现非const和const版本 pop_back()resize()insert()***重点erase()***重点再谈构造函数&#xff01;拷贝构造函数****&#xff08;重点&#xff09;运算符重载***&#xff08;重点&#xff09;…

详解RSA加密算法 | Java模拟实现RSA算法

目录 一.什么是RSA算法 二.RSA算法的算法原理 算法描述 三.RSA算法安全性 四.RSA算法的速度 五.用java实现RSA算法 一.什么是RSA算法 1976年&#xff0c;Diffie和Hellman在文章“密码学新方向&#xff08;New Direction in Cryptography&#xff09;”中首次提出了公开…

arduino - NUCLEO-H723ZG - test

文章目录 arduino - NUCLEO-H723ZG - test概述笔记物理串口软串口备注END arduino - NUCLEO-H723ZG - test 概述 准备向NUCLEO-H723ZG上移植西门子飞达控制的Arduino程序. 先确认一下知识点和效果. 笔记 物理串口 NUCLEO-H723ZG在STM32 Arduino 库中, 只提供了一个串口 Se…

快速了解推荐引擎检索技术

目录 一、推荐引擎和其检索技术 二、推荐引擎的整体架构和工作过程 &#xff08;一&#xff09;用户画像 &#xff08;二&#xff09;文章画像 &#xff08;三&#xff09;推荐算法召回 三、基于内容的召回 &#xff08;一&#xff09;召回算法 &#xff08;二&#xf…

uni-app---- 点击按钮拨打电话功能点击按钮调用高德地图进行导航的功能【安卓app端】

uniapp---- 点击按钮拨打电话功能&&点击按钮调用高德地图进行导航的功能【安卓app端】 先上效果图&#xff1a; 1. 在封装方法的文件夹下新建一个js文件&#xff0c;然后把这些功能进行封装 // 点击按钮拨打电话 export function getActionSheet(phone) {uni.showAct…

【雷达原理】雷达杂波抑制方法

目录 一、杂波及其特点 1.1 什么是杂波&#xff1f; 1.2 杂波的频谱特性 二、动目标显示(MTI)技术 2.1 对消原理 2.2 数字对消器设计 三、MATLAB仿真 3.1 对消效果验证 3.2 代码 一、杂波及其特点 1.1 什么是杂波&#xff1f; 杂波是相对目标回波而言的&#xff0c;…

【Python工具】Panoply介绍及安装步骤

Panoply介绍及安装步骤 1 Panoply介绍2 Panoply安装步骤&#xff08;Windows&#xff09;2.1 下载并安装JAVA环境2.2 下载Panoply报错&#xff1a;Error: A JNI error has occurred, please check your installation and try again. 参考 1 Panoply介绍 Panoply是一款由美国国…