Skip to content

lambda表达式捕获列表?

约 765 字大约 3 分钟

C++金山

2025-03-21

⭐ 题目日期:

金山 - 2024/12/31

📝 题解:

C++中的lambda表达式捕获列表允许访问外部作用域的变量,其使用方式及注意事项如下:

捕获列表的主要方式

  1. 显式捕获

    • [var]:按值捕获变量var
    • [&var]:按引用捕获变量var
  2. 隐式捕获

    • [=]:按值捕获所有外部变量。
    • [&]:按引用捕获所有外部变量。
  3. 混合捕获

    • [=, &var1, &var2]:除var1var2按引用捕获外,其他变量按值捕获。
    • [&, var1, var2]:除var1var2按值捕获外,其他变量按引用捕获。
  4. 初始化捕获(C++14)
    允许在捕获列表中定义新变量并初始化:

    [x = expr]  // 用表达式expr的值初始化变量x(支持移动语义)。
    auto lambda = [value = 10] { return value; };  // 直接定义新变量。
  5. 捕获this指针

    • [this]:捕获当前对象的指针,按引用访问成员变量。
    • [*this](C++17):按值捕获当前对象的副本,避免悬空引用。

关键注意事项

  1. 修改捕获的变量

    • 按值捕获的变量默认是const,需添加mutable关键字才能修改:
      int x = 5;
      auto lambda = [x]() mutable { x += 1; };  // 修改的是副本。
    • 按引用捕获可直接修改原变量,无需mutable
  2. 生命周期管理

    • 按引用捕获时,需确保变量在lambda执行时仍有效,否则导致悬空引用。
    • 按值捕获或初始化捕获(如[*this])可避免生命周期问题。
  3. 全局/静态变量
    无需捕获,可直接访问:

    static int s = 10;
    auto lambda = [] { return s; };  // 无需捕获s。
  4. 避免隐式捕获的风险
    隐式捕获(如[=][&])可能导致意外捕获不需要的变量,建议显式指定关键变量。


示例代码

  1. 值捕获与引用捕获

    int x = 10, y = 20;
    auto lambda1 = [x]() mutable { x += 5; };  // 修改副本x,原x仍为10。
    auto lambda2 = [&y]() { y += 5; };         // 修改原变量y,y变为25。
  2. 混合捕获

    int a = 1, b = 2, c = 3;
    auto lambda = [=, &b] { b = a + c; };  // a和c按值,b按引用。
  3. 初始化捕获(移动语义)

    std::unique_ptr<int> ptr = std::make_unique<int>(42);
    auto lambda = [captured_ptr = std::move(ptr)] { 
        std::cout << *captured_ptr;  // ptr的资源已转移至captured_ptr。
    };
  4. 捕获this*this

    class MyClass {
        int value = 100;
    public:
        void func() {
            auto lambda1 = [this] { value = 200; };  // 修改原对象的value。
            auto lambda2 = [*this]() mutable { 
                value = 200;  // 修改的是副本的value。
            };
        }
    };

总结

  • 按值捕获:安全,但需注意副本的修改(需mutable)。
  • 按引用捕获:高效,但需谨慎生命周期。
  • 初始化捕获(C++14):灵活,支持移动语义和复杂初始化。
  • 混合捕获:结合显式与隐式捕获,灵活控制变量访问方式。
  • 生命周期管理:引用捕获易引发悬空引用,按值或[*this]更安全。

合理选择捕获方式,避免悬空引用和性能问题,是编写健壮lambda表达式的关键。