17370845950

C++ lambda捕获列表 C++ 按值捕获与引用捕获区别【闭包】
按值捕获[x]生成独立副本,但默认const;加mutable才可修改副本,不影响原变量,且拷贝构造函数副作用真实发生。

按值捕获([x])实际是拷贝,不是“只读副本”

很多人误以为 [x] 捕获后 x 就彻底与原变量无关、且不可修改——其实前半对,后半错。按值捕获确实会调用 x 的拷贝构造函数(或移动构造,若满足条件),生成闭包内部的独立副本;但这个副本默认是 const 的,除非 lambda 声明为 mutable

  • 不加 mutable 时,即使捕获的是非 const 变量,也无法在 lambda 体内修改该副本(编译报错:assignment of read-only variable
  • mutable 后,可修改副本,但不影响外部原始变量
  • 若捕获的是类对象,且其拷贝构造函数有副作用(比如日志打印),按值捕获会真实触发它——这点常被忽略
int a = 42;
auto f = [a]() mutable { a = 99; }; // OK:修改的是副本
f();
std::cout << a << "\n"; // 输出 42,原变量未变

引用捕获([&x])绑定的是运行时对象,不是声明时快照

引用捕获不拷贝,只存一个引用,因此闭包内对 x 的读写直接作用于原始变量。但它不保证该变量在 lambda 被调用时仍有效——这是悬垂引用(dangling reference)的高发场景。

  • 若 lambda 在定义它的作用域结束后仍被调用(比如返回给外层、存入容器、交给异步任务),而被捕获的变量已析构,行为未定义
  • 引用捕获对临时对象极其危险:auto f = [&s]{ return s.size(); }; 中若 s 是函数局部 std::string,lambda 返回后调用必崩溃
  • [&] 全局引用捕获更隐蔽:它会把所有自动变量都按引用抓进来,容易无意中延长生命周期依赖

混合捕获时,值捕获和引用捕获共存需显式指定

C++ 不允许隐式混用;必须明确写出每个捕获项。常见错误是想“大部分按引用、个别按值”,却误写成 [&, x][=, &y]——后者在 C++11/14 中非法,C++17 才支持 [=, &y],但 [&, x] 始终合法(先全部引用,再显式覆盖 x 为值捕获)。

  • [&, x]:除 x 外所有变量按引用捕获,x 按值捕获
  • [=, &y]:C++17 起才允许,表示除 y 外所有变量按值捕获,y 按引用捕获
  • 若同时出现 x&x,编译器报错:duplicate capture of 'x'
  • 捕获列表顺序不影响语义,但影响可读性;建议把易出错的引用捕获放在前面并加注释

lambda 生命周期与捕获对象生命周期不匹配是硬伤

没有垃圾回收的 C++ 里,闭包本身不管理捕获对象的生存期。按值捕获靠拷贝“脱钩”,相对安全;引用捕获则完全依赖程序员手动确保“lambda 死亡前,被引对象不能死”。这在回调、线程、信号槽等场景极易翻车。

  • 跨函数传 lambda?检查所有引用捕获变量是否至少活到 lambda 执行完
  • std::thread 拆包 lambda?别直接传 [&x],改用 [x]std

    ::ref(x)
    显式控制
  • 考虑用 std::shared_ptr 包裹共享数据,再按值捕获指针,比裸引用更可控

真正难的不是语法,而是判断“这里该用哪种捕获”——得看变量谁拥有、谁销毁、lambda 谁调用、何时调用。写完 lambda,先盯三秒捕获列表,问自己:这个变量,三年后还在不在?