C++ Lambda 表达式是现代 C++ 中极其强大的特性,它允许在代码中内联定义匿名函数对象。以下是 Lambda 表达式的完整用法指南:


一、基本语法结构

[捕获列表] (参数列表) -> 返回类型 {
    // 函数体
}

简化形式(根据上下文可省略部分):

[]{}          // 最简单的lambda:无参数、无捕获、无返回类型
int x{ return x * x; } // 带参数的lambda

二、捕获列表详解

捕获列表定义了 Lambda 如何访问外部变量。

1. 值捕获 [=]

int a = 1, b = 2;
auto lambda = [=] { 
    return a + b; // 拷贝a和b的值
};
a = 10; // 修改外部a,不影响lambda内的拷贝值
std::cout << lambda(); // 输出 3

2. 引用捕获 [&]

int a = 1, b = 2;
auto lambda = [&] { 
    return a + b; // 引用a和b
};
a = 10; // 修改外部a,lambda内也会看到变化
std::cout << lambda(); // 输出 12

3. 混合捕获

int a = 1, b = 2, c = 3;
auto lambda = [=, &c] { // 值捕获a和b,引用捕获c
    return a + b + c;
};

4. 显式指定捕获变量

int x = 1, y = 2, z = 3;
auto lambda1 = [x, &y] { /* 只捕获x(值)和y(引用) */ };
auto lambda2 = [=, &z] { /* 值捕获所有,但z用引用 */ };
auto lambda3 = [&, x] { /* 引用捕获所有,但x用值 */ };

5. 初始化捕获 (C++14)

int x = 10;
auto lambda = [value = x + 5] { // 用表达式初始化捕获变量
    return value; // value = 15
};

// 移动语义捕获
auto unique_ptr = std::make_unique<int>(42);
auto lambda = [ptr = std::move(unique_ptr)] {
    return *ptr;
};

三、参数列表与返回类型

1. 自动推导返回类型

// 编译器自动推导返回类型
auto square = int x { return x * x; }; // 返回int
auto print = [] { std::cout << "Hello"; }; // 返回void

2. 显式指定返回类型

// 需要显式指定返回类型的情况
auto complex = int x -> double {
    if (x > 0) return x * 1.5;
    else return x * 0.5; // 必须统一返回类型
};

3. 泛型 Lambda (C++14)

// 使用auto参数
auto generic = auto x, auto y { return x + y; };
std::cout << generic(1, 2);       // 3
std::cout << generic(1.5, 2.5);    // 4.0

4. 模板 Lambda (C++20)

// 使用模板语法
auto templateLambda = []<typename T>(T x, T y) {
    return x + y;
};

四、Mutable Lambda

默认情况下,值捕获的变量在 Lambda 内是 const 的,使用 mutable 可以修改:

int counter = 0;
auto lambda =  mutable {
    counter++; // 修改拷贝的值
    return counter;
};

std::cout << lambda(); // 1
std::cout << lambda(); // 2
std::cout << counter;  // 0 (外部变量不变)

五、实际应用场景

1. STL 算法中的谓词

std::vector<int> numbers = {1, 2, 3, 4, 5};

// 查找大于3的第一个元素
auto it = std::find_if(numbers.begin(), numbers.end(),
                      int x { return x > 3; });

// 对每个元素平方
std::transform(numbers.begin(), numbers.end(), numbers.begin(),
              int x { return x * x; });

// 排序:按绝对值大小
std::sort(numbers.begin(), numbers.end(),
         int a, int b { return std::abs(a) < std::abs(b); });

2. 异步编程

#include <future>
#include <iostream>

std::future<int> asyncResult = std::async([]{
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 42;
});

std::cout << "Waiting for result...\n";
std::cout << "Result: " << asyncResult.get() << std::endl;

3. 事件处理与回调

class Button {
public:
    void setOnClick(std::function<void()> callback) {
        m_callback = std::move(callback);
    }
    void click() { if (m_callback) m_callback(); }
private:
    std::function<void()> m_callback;
};

Button btn;
btn.setOnClick([]{
    std::cout << "Button clicked!\n";
});

4. 资源管理 (RAII)

auto fileCloser = FILE* file {
    if (file) fclose(file);
};

// 使用unique_ptr自定义删除器
std::unique_ptr<FILE, decltype(fileCloser)> file(
    fopen("data.txt", "r"), fileCloser);

六、Lambda 的类型与存储

1. 使用 auto 推导

auto lambda = []{ return 42; };
// lambda的类型是编译器生成的唯一类型

2. 使用 std::function 包装

#include <functional>

std::function<int(int)> func = int x { return x * 2; };
// 可以存储任何可调用对象(函数、lambda、函数对象)

3. 作为函数参数传递

void processNumbers(const std::vector<int>& nums,
                  std::function<bool(int)> predicate) {
    for (int n : nums) {
        if (predicate(n)) {
            std::cout << n << " ";
        }
    }
}

processNumbers({1, 2, 3, 4, 5}, int x { return x % 2 == 0; });

七、C++20 新特性

1. 无状态 Lambda 可默认构造

auto lambda = []{ return 42; };
decltype(lambda) lambda2; // C++20 允许:无捕获的lambda可默认构造

2. 模板参数支持

// 模板参数列表
auto lambda = []<typename T>(const std::vector<T>& vec) {
    return vec.size();
};

3. concepts 支持

auto lambda = []<std::integral T>(T x) {
    return x * 2;
};

八、最佳实践与注意事项

  1. 避免过度使用引用捕获:

    // 危险:可能悬空引用
    int* data = new int[100];
    auto dangerous = [&] { use(data); };
    delete[] data; // data成为悬空引用
    dangerous();   // 未定义行为!
    
  2. 优先使用值捕获(除非需要修改外部变量):

    int value = 42;
    auto safe = [value] { return value + 10; }; // 安全
    
  3. 复杂逻辑考虑命名 Lambda:

    auto complexOperation = const auto& data {
        // 复杂的多行逻辑
        if (data.empty()) return false;
        return std::all_of(data.begin(), data.end(),
                          int x { return x > 0; });
    };
    
  4. 性能关键代码避免 std::function:

    // 直接使用auto推导的类型(无开销)
    auto efficient = []{ /* ... */ };
    
    // std::function有类型擦除开销
    std::function<void()> lessEfficient = []{ /* ... */ };
    

完整示例:Lambda 综合应用

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

int main() {
    std::vector<int> numbers = {5, 2, 8, 1, 9, 3};
    
    // 1. 排序:按奇偶性然后大小
    std::sort(numbers.begin(), numbers.end(), int a, int b {
        bool a_even = (a % 2 == 0);
        bool b_even = (b % 2 == 0);
        if (a_even != b_even) {
            return a_even; // 偶数在前
        }
        return a < b;      // 同奇偶性按数值排序
    });
    
    // 2. 过滤并处理:输出大于4的数的平方
    std::for_each(numbers.begin(), numbers.end(), int x {
        if (x > 4) {
            std::cout << x * x << " ";
        }
    });
    
    // 3. 可变Lambda计数器
    int callCount = 0;
    auto countingLambda = int x mutable {
        callCount++;
        return x * 2;
    };
    
    std::cout << "\nCalled " << callCount << " times\n";
    
    return 0;
}

输出结果:

25 64 81 
Called 0 times

Lambda 表达式让 C++ 的函数式编程变得极其便利和强大!

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐