【c++新特性】lambda表达式原理剖析

本文深入剖析C++中Lambda表达式的运作原理,通过实例解释编译器如何将其转化为类及成员函数,帮助读者理解Lambda背后的机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、相关说明

  • 首先这篇博客并不是讲lambda表达式的语法,而是对其背后运作原理的深度剖析。
  • 需要读者具备c++类的基础知识。
  • 只要坚持看完这篇博文,我保证你或多或少肯定会有所收获。
  • 欢迎大家在评论区发表自己的见解,来一场思想的碰撞。

二、问题引入

  • 首先不要着急运行下面的程序,来猜猜这段程序的输出结果将会是什么?并将你猜测的结果发表在评论区。
// lambdaDemo.cpp
#include <iostream>
using namespace std;

int main()
{
    uint32_t numRef = 10, numVal = 20;
    auto func = [&numRef, numVal](string str) mutable {
        ++numVal;
        cout << numRef << " " << numVal << " " << str << endl;
        ++numRef;
    };

    ++numRef;
    ++numVal;
    func("hello");                           
    func("world");                           
    cout << numRef << " " << numVal << endl;
    return 0;
}
  • 猜完了,使用g++ lambdaDemo.cpp -std=c++17 -Wall -o lambdaDemo编译并运行下代码,看看结果。
  • 如果你猜对了,ok。这篇文章对你来说可能意义不是很大。没猜对?强烈建议通读本文。

三、原理解析

1、思考

  • 编译器会将lambda表达式解释为一个类,并且这个类重载了()运算符,这个()就是我们代码中mutable关键字前面的那个。
  • 既然是一个类,那么大家根据运行结果猜一下,在我们代码中,该类 有哪些私有数据成员? 有哪些成员函数? 什么时候创建对象?。你愿意的话,也可以将猜测结果发在评论区。

2、原理揭晓

  • ok,下面我们揭晓答案,请保证你已经思考过上面的问题了,因为这样更有助于你记住下面的结论。
  • 编译器会将[]中捕获的变量,作为类的私有数据成员。并且如果是按引用捕获,那么私有数据成员也是引用类型,按值捕获,私有数据成员就是一般变量。并且该类会生成一个构造函数,其参数就是我们捕获的变量。结果如下:
class __lambda_7_17
{
private:
    uint32_t &numRef;
    uint32_t numVal;

public:
    __lambda_7_17(uint32_t &_numRef, uint32_t &_numVal)
        : numRef{_numRef}, numVal{_numVal}
    {
    }
};
  • 编译器会生成一个重载()的成员函数,这个函数的函数体,就是我们lambda表达式的函数体。如下:
class __lambda_7_17
    {
    public:
        inline void operator()(std::basic_string<char> str)
        {
            ++numVal;
            cout << numRef << " " << numVal << " " << str << endl;
            ++numRef;
        }
  • 当这个lambda表达式被定义之后,就会马上创建该类的对象。注意并不是在调用该lambda时才创建对象。
  • 最后,这是我们上面的代码被翻译成类之后的结果,使用的工具cppinsights
#include <iostream>
using namespace std;

int main()
{
    uint32_t numRef = 10;
    uint32_t numVal = 20;

    class __lambda_7_17
    {
    public:
        inline void operator()(std::basic_string<char> str)
        {
            ++numVal;
            std::operator<<(std::operator<<(std::operator<<(std::cout.operator<<(numRef), " ").operator<<(numVal), " "), str).operator<<(std::endl);
            ++numRef;
        }

    private:
        uint32_t &numRef;
        uint32_t numVal;

    public:
        __lambda_7_17(uint32_t &_numRef, uint32_t &_numVal)
            : numRef{_numRef}, numVal{_numVal}
        {
        }
    };

    __lambda_7_17 func = __lambda_7_17{numRef, numVal};
    ++numRef;
    ++numVal;
    func.operator()(std::basic_string<char>("hello", std::allocator<char>()));
    func.operator()(std::basic_string<char>("world", std::allocator<char>()));
    std::operator<<(std::cout.operator<<(numRef), " ").operator<<(numVal).operator<<(std::endl);
    return 0;
}

四、进一步思考

  • 代码的运行结果我就不贴在这里了,相信大家都已经知道了,还没知道的同学赶紧一键三连,有空在回头看看。
  • 再留一些问题,引导大家进一步思考,加深对lambda的理解。
    • 为什么明明numRef和numVal被++的次数是一样的,而最后打印出的numRef被加了3,而numVal只加了1?
    • 如果我把第14行的++numRef改为numRef = 0,最终结果会变成啥?
  • 记得把问题答案贴在评论区哦!

觉得不错请点赞👍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr成文

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值