C++11 Lambda表达式的本质是什么?它的捕获列表 ([]) 是如何工作的?

C++11 Lambda表达式的本质是什么?它的捕获列表 ([]) 是如何工作的?

Lambda表达式本质上是创建匿名函数对象(Functor) 的一种便捷语法糖。捕获列表 [] 则决定了外部变量如何被这个函数对象"记住"

Lambda的本质:一个匿名的函数对象

下一个Lambda表达式时,编译器在底层会自动生成一个匿名的struct或class ,并重载其函数调用运算符 operator()

eg:

c++ 复制代码
auto my_lambda = [](int x) { return x * 2; };

编译器在背后生成的代码,大致等价于下面这个函数对象:

c++ 复制代码
class __Lambda_Anonymous_Class__ {
public:
    // 重载 operator(),使其行为与Lambda体一致
    int operator()(int x) const {
        return x * 2;
    }
};

// 创建这个匿名类的实例
auto my_lambda = __Lambda_Anonymous_Class__{};

my_lambda(5) 实际上是调用了 __Lambda_Anonymous_Class__ 实例的 operator() 方法。这就是Lambda表达式的本质


捕获列表 [] 的工作原理

捕获列表是Lambda最强大的功能之一,它决定了Lambda如何"捕获"其所在作用域的外部变量。捕获的变量会成为上面那个匿名函数对象类的成员变量

按值捕获 [=] 或 [var]:

按值捕获会将被捕获变量的一份拷贝存为匿名函数对象的成员变量

  1. [=]:捕获所有在Lambda体内用到的外部变量(按值)
  2. [var]:只捕获名为var的变量(按值)
c++ 复制代码
int main() {
    int y = 10;
    
    // 按值捕获 y
    auto add_y = [y](int x) {
        return x + y; // 这里的y是main函数里y的一份拷贝
    };

    std::cout << add_y(5) << std::endl; // 输出 15

    y = 20; // 修改原始的y
    std::cout << add_y(5) << std::endl; // 仍然输出 15,因为Lambda持有的是y=10时的拷贝
}

底层等价物:

c++ 复制代码
class __Add_Y_Functor__ {
private:
    int y_copy_; // 按值捕获,成为成员变量

public:
    __Add_Y_Functor__(int y_val) : y_copy_(y_val) {} // 构造时拷贝

    int operator()(int x) const {
        return x + y_copy_;
    }
};

auto add_y = __Add_Y_Functor__(y); // y被拷贝到构造函数里

按引用捕获 [&] 或 [&var]

  1. [&]:捕获所有在Lambda体内用到的外部变量(按引用)
  2. [&var]:只捕获名为var的变量(按引用)
c++ 复制代码
int main() {
    int y = 10;
    
    // 按引用捕获 y
    auto add_y_ref = [&y](int x) {
        return x + y; // 这里的y是main函数里y的引用
    };

    std::cout << add_y_ref(5) << std::endl; // 输出 15

    y = 20; // 修改原始的y
    std::cout << add_y_ref(5) << std::endl; // 输出 25,因为Lambda持有的是y的引用
}

底层等价物:

c++ 复制代码
class __Add_Y_Ref_Functor__ {
private:
    int& y_ref_; // 按引用捕获,成为引用成员

public:
    __Add_Y_Ref_Functor__(int& y_val) : y_ref_(y_val) {}

    int operator()(int x) const {
        return x + y_ref_;
    }
};

auto add_y_ref = __Add_Y_Ref_Functor__(y);

按引用捕获要非常小心,如果Lambda的生命周期超过了被引用变量的生命周期,会导致悬垂引用 (Dangling Reference)

mutable 关键字:

默认情况下,按值捕获的成员变量在Lambda体内是const的,不能被修改。如果想修改这些拷贝,需要使用mutable关键字。mutable会移除operator()的const限定

c++ 复制代码
int counter = 0;
auto my_counter = [counter]() mutable {
    counter++; // 正确!可以修改拷贝的counter
    return counter;
};

std::cout << my_counter() << std::endl; // 输出 1
std::cout << my_counter() << std::endl; // 输出 2
std::cout << counter << std::endl; // 输出 0, 原始的counter未受影响
相关推荐
余额瞒着我当琳3 小时前
红黑树的实现--红黑树的概念、规则、效率、结构、旋转、查找、验证
数据结构·c++·算法
青少儿编程课堂3 小时前
AC自动机多模式串匹配精讲:校园关键词巡检实战解析
c++·python·算法·bfs·信息学竞赛
longlongzihan3 小时前
LeetCode 5. 最长回文子串:从暴力匹配到中心扩展的思维演进
c++·算法·leetcode
多弗朗皮卡丘4 小时前
C++继承
c++·继承
汉克老师4 小时前
GESP2026年9月认证C++七级( 第三部分编程题(2、括号序列))精讲
c++·gesp·小学生·学c++编程
jimy16 小时前
虚函数vtable,链接时看到构造函数,linker把符号地址写入vtable(三)
开发语言·c++
潼心1412o7 小时前
C++初阶(长期更新)第9讲:string(上)
开发语言·c++
朝朝辞暮i7 小时前
C++ 第 39 章: 阶段性总复习——类、对象、构造、继承、this、指针与智能指针
开发语言·c++·算法·ros2
wuminyu8 小时前
Virtual Thread重投递至ForkJoinPool任务队列过程解析
java·linux·c语言·jvm·c++
All for pursuit.8 小时前
【回溯-6】79.单词搜索
数据结构·c++·算法·leetcode