C++ -- lambda捕获

C++ Lambda 捕获分为‌基础捕获 ‌、‌混合捕获 ‌、‌特殊捕获 ‌和‌C++14/20 新特性‌。

1. 基础捕获:值 vs 引用

A. 空捕获 []

不访问任何外部变量。

cpp 复制代码
int x = 10;
auto f = ‌:ml-search[] { 
    // std::cout << x; // 错误!x 未捕获
    return 42; 
};
B. 按值捕获 [x]

拷贝外部变量。‌内部修改不影响外部 ‌,且默认不可修改(除非加 mutable)。

cpp 复制代码
int x = 10;
auto f = ‌:ml-search[x] { 
    // x = 20; // 错误!按值捕获的变量是 const 的
    return x; 
};
// x 仍然是 10
C. 按值捕获 + mutable [x] mutable

允许修改‌副本 ‌,但‌不影响外部变量‌。

cpp 复制代码
int x = 10;
auto f = ‌:ml-search[x] mutable { 
    x = 20; // 合法,修改的是内部副本
    return x; 
};
std::cout << f() << std::endl; // 输出 20
std::cout << x << std::endl;   // 输出 10 (外部未变)
D. 按引用捕获 [&x]

直接引用外部变量。‌内部修改直接影响外部‌。需注意生命周期(悬空引用风险)。

cpp 复制代码
int x = 10;
auto f = ‌:ml-search[&x] { 
    x = 20; // 合法,直接修改外部 x
    return x; 
};
f();
std::cout << x << std::endl; // 输出 20 (外部已变)

2. 默认捕获与混合捕获(最常用)

E. 默认按值捕获 [=]

Lambda 体内用到的所有局部变量都按值拷贝。

cpp 复制代码
int a = 1, b = 2;
auto f = ‌:ml-search[=] { 
    return a + b; 
};

注意:在 C++11/14 中,[=] 也会隐式捕获 this 指针(如果使用了成员变量)。

F. 默认按引用捕获 [&]

Lambda 体内用到的所有局部变量都按引用绑定。

cpp 复制代码
int a = 1, b = 2;
auto f = ‌:ml-search[&] { 
    a++; b++; // 直接修改外部变量
};
G. 混合捕获:默认值 + 特定引用 [=, &x]

场景‌:大部分变量只需读取(拷贝开销小或需要快照),但某个大对象或需要修改的变量使用引用。

cpp 复制代码
int a = 1;
std::vector<int> huge_vec(1000000); // 大对象
auto f = ‌:ml-search[=, &huge_vec] { 
    // a 是拷贝(安全,快照)
    // huge_vec 是引用(避免拷贝百万级数据,且可修改)
    huge_vec.push_back(a); 
};
H. 混合捕获:默认引用 + 特定值 [&, x]

场景‌:大部分变量需要修改外部状态,但某个变量(如 ID、配置)需要保留创建时的副本,防止外部意外修改。

cpp 复制代码
int id = 100;
int status = 0;
auto f = ‌:ml-search[&, id] { 
    status = 1; // 修改外部 status
    // id = 200; // 错误!id 是 const 副本
    std::cout << "ID at creation: " << id << std::endl;
};
id = 999; // 外部修改 id
f();      // 输出: ID at creation: 100 (内部仍保留旧值)

3. 类成员与 this 指针

I. 捕获 this [this]

在类成员函数中,若要访问成员变量,必须捕获 this

cpp 复制代码
class MyClass {
    int val = 10;
public:
    void doWork() {
        // auto f = ‌:ml-search[val] {}; // 错误!C++11/14/17 不能直接捕获成员变量
        auto f = ‌:ml-search[this] { 
            val = 20; // 通过 this->val 访问
        };
        f();
    }
};

风险:如果 MyClass 对象销毁了,但 Lambda 还被持有(例如在线程池中),调用 Lambda 会导致 Crash。

J. C++20 新特性:按值捕获 *this [*this]

解决上述风险‌。它会拷贝整个对象到 Lambda 内部。

cpp 复制代码
// C++20
auto f = ‌:ml-search[*this] { 
    val = 20; // 修改的是 Lambda 内部拷贝的对象,不影响原对象
};

优点:安全,无悬空指针。缺点:如果对象很大,拷贝开销大。

4. C++14 初始化捕获(Init Capture / Generalized Capture)

这是 C++14 引入的强大功能,允许在捕获列表中‌定义新变量‌并初始化。它解决了"移动语义"和"重命名"的问题。

K. 移动捕获(Move Capture)

用于捕获不可拷贝的对象(如 std::unique_ptr)。

cpp 复制代码
auto ptr = std::make_unique<int>(100);
// auto f = ‌:ml-search[ptr] {}; // 错误!unique_ptr 不可拷贝
auto f = ‌:ml-search[p = std::move(ptr)] { 
    std::cout << *p << std::endl; 
};
// ptr 现在为空,所有权已转移给 Lambda 内部的 p
L. 捕获表达式结果

可以捕获任意表达式的结果,甚至给变量起别名。

cpp 复制代码
int x = 10;
// 捕获 x 的两倍,并存入名为 doubled_x 的新变量
auto f = ‌:ml-search[doubled_x = x * 2] { 
    return doubled_x; 
};
M. 捕获引用(C++14)

虽然 C++11 有 [&x],但 C++14 允许更灵活的引用初始化。

cpp 复制代码
int x = 10;
auto f = ‌:ml-search[&ref_x = x] { 
    ref_x = 20; 
};
相关推荐
见过夏天9 小时前
C++ 基础入门完全指南
c++
用户805533698032 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
BadBadBad__AK2 天前
线段树维护区间 k 次方和
c++·数学·算法·stl
卷无止境3 天前
Eigen 库如何借助 OpenMP 加速计算
c++·后端
卷无止境3 天前
OpenMPI、MPICH 与 OpenMP:关系、核心概念与架构全解
c++·后端
郝学胜_神的一滴4 天前
CMake 30:循环语法全解|foreach_while双循环精讲、迭代技巧与实战避坑指南
c++·cmake
卷无止境6 天前
C++ 的Eigen 库全解析
c++
卷无止境6 天前
现代 C++特性大盘点:一门脱胎换骨的老语言
c++·后端
郝学胜_神的一滴6 天前
CMake 27:缓存变量的特性、语法、类型与实操全解
c++·cmake
博客18008 天前
酷宝的使用方法,超好用的免费界面库,C++、MFC可用
c++·mfc·界面库·库来帮·酷宝