C++ Lambda 表达式的本质及原理分析

目录

1.引言

2.Lambda 的本质

3.Lambda 的捕获机制的本质

4.捕获方式的实现与底层原理

5.默认捕获的实现原理

6.捕获 this 的机制

7.捕获的限制与注意事项

8.总结


1.引言

        C++ 中的 Lambda 表达式是一种匿名函数,最早在 C++11 引入,用于简化函数对象的定义和使用。它以更简洁的语法提供了强大的功能,但其本质和捕获机制背后有许多值得深究的细节。本文将探讨 Lambda 的本质,以及捕获的底层实现与原理。

2.Lambda 的本质

        Lambda 是一个语法糖,本质上是由编译器生成的一个匿名类,该类重载了 operator()(即调用运算符)。在使用 Lambda 表达式时,编译器会隐式生成一个这样的类,并在必要时捕获上下文中的变量。

        示例与编译器生成的代码对比

#include <iostream>
#include <functional>

int main() {
    int x = 10;
    auto lambda = [x](int y) { return x + y; };
    std::cout << lambda(20) << std::endl; // 输出 30
    return 0;
}

编译器会将上述 Lambda 转换为类似以下的代码:

#include <iostream>
#include <functional>

class LambdaClass {
    int x;
public:
    LambdaClass(int x) : x(x) {}
    int operator()(int y) const {
        return x + y;
    }
};

int main() {
    int x = 10;
    LambdaClass lambda(x);
    std::cout << lambda(20) << std::endl; // 输出 30
    return0;
}

可以看到,Lambda 实际上是一个具有捕获变量 x 的函数对象。

3.Lambda 的捕获机制的本质

Lambda 的捕获机制允许其在定义时绑定外部作用域中的变量,以便在 Lambda 内部使用。这一机制本质上是通过捕获变量并存储为匿名类的成员变量来实现的。

捕获的两种方式

1)值捕获(capture by value): 捕获外部变量的副本,保存在 Lambda 的内部。

2)引用捕获(capture by reference): 捕获外部变量的引用,Lambda 内部直接访问外部变量。

4.捕获方式的实现与底层原理

1)值捕获的实现 值捕获会在 Lambda 表达式创建时,将捕获的变量拷贝到匿名类的成员变量中。每次调用 Lambda 时,使用的是捕获时的副本。

#include <iostream>

int main() {
    int x = 10;
    auto lambda = [x]() { std::cout << x << std::endl; };
    x = 20;
    lambda(); // 输出 10,而非 20
    return 0;
}

编译器生成的代码类似于:

class Lambda {
    int x; // 保存捕获的副本
public:
    Lambda(int x) : x(x) {}
    void operator()() const {
        std::cout << x << std::endl;
    }
};

这里,x 是一个副本,与原始变量脱离关系。

2)引用捕获的实现 引用捕获则是将外部变量的引用存储为 Lambda 类的成员变量,调用时直接操作原变量。

#include <iostream>

int main() {
    int x = 10;
    auto lambda = [&x]() { std::cout << x << std::endl; };
    x = 20;
    lambda(); // 输出 20
    return 0;
}

编译器生成的代码类似于:

class Lambda {
    int& x; // 保存外部变量的引用
public:
    Lambda(int& x) : x(x) {}
    void operator()() const {
        std::cout << x << std::endl;
    }
};

可以看到,引用捕获直接存储的是外部变量的引用,Lambda 的调用会影响原变量。

5.默认捕获的实现原理

1)默认值捕获 [=] 使用 [=] 会默认按值捕获外部作用域的所有变量。

int x = 10, y = 20;
auto lambda = [=]() { return x + y; }; // 默认值捕获 x 和 y

等价于:

class Lambda {
    int x, y;
public:
    Lambda(int x, int y) : x(x), y(y) {}
    int operator()() const {
        return x + y;
    }
};

2)默认引用捕获 [&] 使用 [&] 会默认按引用捕获外部作用域的所有变量。

int x = 10, y = 20;
auto lambda = [&]() { return x + y; }; // 默认引用捕获 x 和 y

等价于:

class Lambda {
    int& x, & y;
public:
    Lambda(int& x, int& y) : x(x), y(y) {}
    int operator()() const {
        return x + y;
    }
};

6.捕获 this 的机制

        捕获 this 时,实际上是按值捕获了 this 指针,使得 Lambda 可以访问当前对象的成员变量。如果捕获 *this,则表示按值捕获整个对象。

        示例:捕获 this

#include <iostream>

class MyClass {
    int data = 42;
public:
    auto createLambda() {
        return [this]() { std::cout << data << std::endl; };
    }
};

int main() {
    MyClass obj;
    auto lambda = obj.createLambda();
    lambda(); // 输出 42
    return0;
}

编译器生成的代码类似于:

class Lambda {
    MyClass* obj; // 捕获 this 指针
public:
    Lambda(MyClass* obj) : obj(obj) {}
    void operator()() const {
        std::cout << obj->data << std::endl;
    }
};

7.捕获的限制与注意事项

1)不能捕获动态生成的变量: Lambda 只能捕获作用域中已有的变量,不能捕获运行时动态生成的变量。

2)捕获的生命周期: 引用捕获的变量必须保证 Lambda 的生命周期不超过捕获对象。

3)与 mutable 相关的限制: 捕获的变量默认是不可变的(即 const)。如果需要修改捕获的变量,需要显式添加 mutable

8.总结

1)Lambda 的本质: 是一个匿名类,其捕获的变量存储为类的成员变量,调用时通过重载的 operator() 实现。

2)捕获的本质: 值捕获是将外部变量的副本存储为类成员,引用捕获是将外部变量的引用存储为类成员。

3)注意事项: 使用 Lambda 时,需要特别关注变量的生命周期和捕获方式,以避免未定义行为。

Lambda 表达式在 C++ 中提供了极大的灵活性和简洁性,特别是在需要定义短小的回调函数或处理算法时。理解并熟练使用 Lambda 表达式可以显著提升代码的可读性和效率。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

最优化问题 - 内点法

以下是一种循序推理的方式&#xff0c;来帮助你从基础概念出发&#xff0c;理解 内点法&#xff08;Interior-Point Method, IPM&#xff09; 是什么、为什么要用它&#xff0c;以及它是如何工作的。 1. 问题起点&#xff1a;带不等式约束的优化 假设你有一个带不等式约束的优…

Linux下Ubuntun系统报错find_package(BLAS REQUIRED)找不到

Linux下Ubuntun系统报错find_package(BLAS REQUIRED)找不到 这次在windows的WSL2中遇到了一个非常奇怪的错误&#xff0c;就是 CMake Error at /usr/share/cmake-3.22/Modules/FindPackageHandleStandardArgs.cmake:230 (message):Could NOT find BLAS (missing: BLAS_LIBRAR…

Ubuntu Server 安装 XFCE4桌面

Ubuntu Server没有桌面环境&#xff0c;一些软件有桌面环境使用起来才更加方便&#xff0c;所以我尝试安装桌面环境。常用的桌面环境有&#xff1a;GNOME、KDE Plasma、XFCE4等。这里我选择安装XFCE4桌面环境&#xff0c;主要因为它是一个极轻量级的桌面环境&#xff0c;适合内…

芯片AI深度实战:实战篇之vim chat

利用vim-ollama这个vim插件&#xff0c;可以在vim内和本地大模型聊天。 系列文章&#xff1a; 芯片AI深度实战&#xff1a;基础篇之Ollama-CSDN博客 芯片AI深度实战&#xff1a;基础篇之langchain-CSDN博客 芯片AI深度实战&#xff1a;实战篇之vim chat-CSDN博客 芯片AI深度…

线程概念、操作

一、背景知识 1、地址空间进一步理解 在父子进程对同一变量进行修改时发生写时拷贝&#xff0c;这时候拷贝的基本单位是4KB&#xff0c;会将该变量所在的页框全拷贝一份&#xff0c;这是因为修改该变量很有可能会修改其周围的变量&#xff08;局部性原理&#xff09;&#xf…

设置jmeter外观颜色

设置jmeter外观颜色 方法&#xff1a; 步骤一、点击顶部选项 ->外观&#xff0c;这里提供了不同的主题&#xff0c;可选自己喜欢的风格。 步骤二、选择后&#xff0c;弹框提示点击Yes。

2021 年 6 月大学英语四级考试真题(第 1 套)——纯享题目版

&#x1f3e0;个人主页&#xff1a;fo安方的博客✨ &#x1f482;个人简历&#xff1a;大家好&#xff0c;我是fo安方&#xff0c;目前中南大学MBA在读&#xff0c;也考取过HCIE Cloud Computing、CCIE Security、PMP、CISP、RHCE、CCNP RS、PEST 3等证书。&#x1f433; &…

[论文总结] 深度学习在农业领域应用论文笔记14

当下&#xff0c;深度学习在农业领域的研究热度持续攀升&#xff0c;相关论文发表量呈现出迅猛增长的态势。但繁荣背后&#xff0c;质量却不尽人意。相当一部分论文内容空洞无物&#xff0c;缺乏能够落地转化的实际价值&#xff0c;“凑数” 的痕迹十分明显。在农业信息化领域的…

LangGraph系列-1:用LangGraph构建简单聊天机器人

在快速发展的人工智能和大型语言模型&#xff08;llm&#xff09;世界中&#xff0c;开发人员不断寻求创建更灵活、更强大、更直观的人工智能代理的方法。 虽然LangChain已经改变了这个领域的游戏规则&#xff0c;允许创建复杂的链和代理&#xff0c;但对代理运行时的更复杂控制…

【hot100】刷题记录(7)-除自身数组以外的乘积

题目描述&#xff1a; 给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#x…

OpenCV边沿检测(Python版)

边缘检测是图像处理中的一项重要任务&#xff0c;用于找到图像中的边界或边缘。它在计算机视觉、图像处理和模式识别等领域中具有广泛的应用。 边缘可以被定义为图像亮度、颜色或纹理的突变区域。边缘检测算法旨在识别这些变化并将其标记为边缘。边缘检测可以用于分割图像、检测…

推荐七节来自NVIDIA、Google、斯坦福的AI课程

英伟达 &#xff08;1&#xff09;在 10 分钟内构建大脑 • 探索神经网络如何使用数据进行学习。 • 了解神经元背后的数学原理。 链接&#xff1a;https://learn.nvidia.com/courses/course-detail?course_idcourse-v1:DLIT-FX-01V1 &#xff08;2&#xff09;构建视频 A…

《从因果关系的角度学习失真不变表示以用于图像恢复》学习笔记

paper&#xff1a;2303.06859 GitHub&#xff1a;lixinustc/Causal-IR-DIL: Distortion invariant feature learning for image restoration from a causality perspective 2023 CVPR 摘要 近年来&#xff0c;我们见证了深度神经网络&#xff08;DNNs&#xff09;在图像恢复…

亚博microros小车-原生ubuntu支持系列:16 机器人状态估计

本来想测试下gmapping建图&#xff0c;但是底层依赖了yahboomcar_bringup做底层的数据处理&#xff0c;所以先把依赖的工程导入。 程序启动后&#xff0c;会订阅imu和odom数据&#xff0c;过滤掉一部分的imu数据后&#xff0c;然后与odom数据进行融合&#xff0c;最后输出一个…

不背单词快捷键(不背单词键盘快捷键)

文章目录 不背单词快捷键 不背单词快捷键 ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ    …

VPR概述、资源

SOTA网站&#xff1a; Visual Place Recognition | Papers With Code VPR&#xff08;Visual Place Recognition&#xff09; 是计算机视觉领域的一项关键任务&#xff0c;旨在通过图像匹配和分析来识别场景或位置。它的目标是根据视觉信息判断某个场景是否与数据库中的场景匹…

MYSQL 商城系统设计 商品数据表的设计 商品 商品类别 商品选项卡 多表查询

介绍 在开发商品模块时&#xff0c;通常使用分表的方式进行查询以及关联。在通过表连接的方式进行查询。每个商品都有不同的分类&#xff0c;每个不同分类下面都有商品规格可以选择&#xff0c;每个商品分类对应商品规格都有自己的价格和库存。在实际的开发中应该给这些表进行…

代理模式 -- 学习笔记

代理模式学习笔记 什么是代理&#xff1f; 代理是一种设计模式&#xff0c;用户可以通过代理操作&#xff0c;而真正去进行处理的是我们的目标对象&#xff0c;代理可以在方法增强&#xff08;如&#xff1a;记录日志&#xff0c;添加事务&#xff0c;监控等&#xff09; 拿一…

Flutter使用Flavor实现切换环境和多渠道打包

在Android开发中通常我们使用flavor进行多渠道打包&#xff0c;flutter开发中同样有这种方式&#xff0c;不过需要在原生中配置 具体方案其实flutter官网个了相关示例&#xff08;https://docs.flutter.dev/deployment/flavors&#xff09;,我这里记录一下自己的操作 Android …

WinDBG查找C++句柄泄露

C代码&#xff08;频繁点击About按钮导致Mutex句柄泄露&#xff09; HANDLE _mutexHandle;LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {switch (message){case WM_COMMAND:{int wmId LOWORD(wParam);// 分析菜单选择:switch (wmId){c…