Lambda 表达式是 C++11 引入的一项强大特性,它允许你在需要一个函数的地方内联定义一个匿名函数。你可以把它理解为一个 “一次性” 的、临时的函数,非常适合用于编写简短、只在局部使用的代码片段,尤其是在结合 STL 算法时。

为什么需要 Lambda 表达式?

在 Lambda 出现之前,如果想给 std::sort 或 std::for_each 这样的算法传递一个自定义的比较或操作逻辑,你通常需要:

  1. 定义一个全局函数:这会污染全局命名空间,而且函数可能只在一个地方使用。
  2. 定义一个函数对象(Functor):代码结构相对复杂,需要创建一个类。

Lambda 表达式解决了这些问题,它让代码:

  • 更简洁:将函数逻辑直接写在使用它的地方,无需额外定义。
  • 更清晰:读者可以在调用点立即看到函数的实现,而不需要跳转到其他地方。
  • 更灵活:可以方便地捕获(capture)周围作用域的变量。

Lambda 表达式的语法

Lambda 表达式的语法可以分解为以下几个部分:

[capture-clause] (parameters) -> return-type { body }

让我们逐一解析:

  1. [capture-clause] (捕获子句): 这是 Lambda 表达式最核心的特性之一。它定义了 Lambda 可以从其外围作用域(即定义 Lambda 的函数或代码块)中捕获哪些变量,以及如何捕获(值捕获或引用捕获)。

    • []: 空捕获列表。Lambda 不捕获任何外部变量。
    • [=]: 以值传递的方式捕获所有外部变量。Lambda 内部会创建这些变量的副本。
    • [&]: 以引用传递的方式捕获所有外部变量。Lambda 内部使用的是这些变量的引用。
    • [var]: 以值传递的方式捕获指定变量 var
    • [&var]: 以引用传递的方式捕获指定变量 var
    • [this]: 在类的成员函数中,捕获当前对象的指针 this,允许 Lambda 访问类的成员变量和成员函数。
    • [=, &var]: 以值传递捕获所有外部变量,但对 var 变量使用引用传递。
    • [&, var]: 以引用传递捕获所有外部变量,但对 var 变量使用值传递。
  2. (parameters) (参数列表): 这和普通函数的参数列表类似。如果 Lambda 没有参数,可以省略这部分。

    • (): 无参数。
  3. -> return-type (返回类型): 这部分指定了 Lambda 表达式的返回类型。在很多情况下,编译器可以根据 return 语句自动推导返回类型,因此这部分也可以省略。

    • 如果 Lambda 体中包含多个 return 语句,且返回类型不同,编译器可能无法推导,此时必须显式指定返回类型。
  4. { body } (函数体): 这是 Lambda 表达式的执行代码块,和普通函数体一样。

2. 完整的Lambda语法分解

cpp

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

void basicSyntax() {
    cout << "=== Lambda表达式完整语法 ===" << endl;
    
    // 1. 最简单的lambda - 无参数,无返回值推导
    auto lambda1 = []() {
        cout << "Hello Lambda!" << endl;
    };
    lambda1();
    
    // 2. 带参数的lambda
    auto lambda2 = [](int a, int b) {
        return a + b;
    };
    cout << "5 + 3 = " << lambda2(5, 3) << endl;
    
    // 3. 显式指定返回类型
    auto lambda3 = [](double a, double b) -> double {
        return a / b;
    };
    cout << "10.0 / 3.0 = " << lambda3(10.0, 3.0) << endl;
    
    // 4. 参数可以auto推导 (C++14)
    auto lambda4 = [](auto a, auto b) {
        return a + b;
    };
    cout << "auto参数: " << lambda4(5, 3.5) << endl;  // 8.5
}

3. 捕获列表详解

捕获列表是Lambda最强大的特性之一,它决定了Lambda如何访问外部变量。

值捕获 vs 引用捕获

cpp

void captureDemo() {
    cout << "\n=== 捕获列表详解 ===" << endl;
    
    int x = 10;
    int y = 20;
    string name = "C++";
    
    // 1. 值捕获 [=] 或具体变量
    auto value_capture = [x, y]() {
        cout << "值捕获: x=" << x << ", y=" << y << endl;
        // x = 100; // 错误!值捕获的变量是const的
    };
    value_capture();
    
    // 2. 引用捕获 [&] 或具体变量
    auto ref_capture = [&x, &name]() {
        cout << "引用捕获修改前: x=" << x << ", name=" << name << endl;
        x = 100;
        name = "Lambda";
        cout << "引用捕获修改后: x=" << x << ", name=" << name << endl;
    };
    ref_capture();
    cout << "外部变量也被修改: x=" << x << ", name=" << name << endl;
    
    // 3. 混合捕获
    auto mixed_capture = [x, &y, name]() {
        cout << "混合捕获: x(值)=" << x << ", y(引用)=" << y << ", name(值)=" << name << endl;
        y = 200;  // 可以修改引用捕获的变量
    };
    mixed_capture();
    cout << "y被修改为: " << y << endl;
    
    // 4. 默认捕获
    int a = 1, b = 2, c = 3;
    
    // 默认值捕获 [=]
    auto default_by_value = [=]() {
        cout << "默认值捕获: a=" << a << ", b=" << b << ", c=" << c << endl;
    };
    default_by_value();
    
    // 默认引用捕获 [&]
    auto default_by_ref = [&]() {
        a++; b++; c++;
        cout << "默认引用捕获修改: a=" << a << ", b=" << b << ", c=" << c << endl;
    };
    default_by_ref();
    cout << "外部变量: a=" << a << ", b=" << b << ", c=" << c << endl;
}

可变Lambda (mutable)

cpp

void mutableLambda() {
    cout << "\n=== 可变Lambda (mutable) ===" << endl;
    
    int count = 0;
    
    // 没有mutable - 值捕获的变量是const
    // auto lambda1 = [count]() {
    //     count++;  // 错误!不能修改值捕获的变量
    // };
    
    // 使用mutable - 可以修改值捕获的变量(但修改的是副本)
    auto lambda2 = [count]() mutable {
        count++;
        cout << "内部count: " << count << endl;
        return count;
    };
    
    cout << "调用前外部count: " << count << endl;
    lambda2();  // 内部count: 1
    lambda2();  // 内部count: 2  
    lambda2();  // 内部count: 3
    cout << "调用后外部count: " << count << endl;  // 仍然是0!
    
    // 引用捕获不需要mutable
    auto lambda3 = [&count]() {
        count++;
        cout << "引用捕获修改count: " << count << endl;
    };
    lambda3();  // count: 1
    lambda3();  // count: 2
    cout << "最终外部count: " << count << endl;  // 2
}

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

cpp

void initCapture() {
    cout << "\n=== 初始化捕获 (C++14) ===" << endl;
    
    int x = 100;
    
    // 1. 在捕获时初始化变量
    auto lambda1 = [value = x + 50]() {
        cout << "初始化捕获: value = " << value << endl;
    };
    lambda1();
    
    // 2. 移动语义捕获
    auto unique_ptr = make_unique<int>(42);
    auto lambda2 = [ptr = move(unique_ptr)]() {
        if (ptr) {
            cout << "移动捕获的值: " << *ptr << endl;
        }
    };
    lambda2();
    // cout << *unique_ptr << endl;  // 错误!unique_ptr已经被移动
    
    // 3. 引用初始化捕获
    string name = "Hello";
    auto lambda3 = [&ref = name]() {
        ref += " World";
        cout << "引用初始化捕获: " << ref << endl;
    };
    lambda3();
    cout << "外部name: " << name << endl;
}

5. Lambda在STL算法中的应用

cpp

#include <algorithm>
#include <numeric>
#include <vector>

void stlWithLambda() {
    cout << "\n=== Lambda在STL算法中的应用 ===" << endl;
    
    vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // 1. for_each
    cout << "for_each: ";
    for_each(numbers.begin(), numbers.end(), [](int n) {
        cout << n << " ";
    });
    cout << endl;
    
    // 2. find_if - 查找第一个偶数
    auto it = find_if(numbers.begin(), numbers.end(), [](int n) {
        return n % 2 == 0;
    });
    if (it != numbers.end()) {
        cout << "第一个偶数: " << *it << endl;
    }
    
    // 3. count_if - 统计满足条件的元素
    int count = count_if(numbers.begin(), numbers.end(), [](int n) {
        return n > 5;
    });
    cout << "大于5的元素个数: " << count << endl;
    
    // 4. transform - 转换元素
    vector<int> squared(numbers.size());
    transform(numbers.begin(), numbers.end(), squared.begin(), [](int n) {
        return n * n;
    });
    
    cout << "平方后的数组: ";
    for (int n : squared) cout << n << " ";
    cout << endl;
    
    // 5. sort - 自定义排序
    vector<string> words = {"apple", "banana", "cherry", "date", "elderberry"};
    sort(words.begin(), words.end(), [](const string& a, const string& b) {
        return a.length() < b.length();  // 按长度排序
    });
    
    cout << "按长度排序: ";
    for (const auto& word : words) cout << word << " ";
    cout << endl;
    
    // 6. accumulate - 自定义累加操作
    vector<int> values = {1, 2, 3, 4, 5};
    int product = accumulate(values.begin(), values.end(), 1, [](int a, int b) {
        return a * b;
    });
    cout << "乘积: " << product << endl;
}

6. 泛型Lambda (C++14)

cpp

void genericLambda() {
    cout << "\n=== 泛型Lambda (C++14) ===" << endl;
    
    // 1. 使用auto参数
    auto generic_add = [](auto a, auto b) {
        return a + b;
    };
    
    cout << "int加法: " << generic_add(5, 3) << endl;           // 8
    cout << "double加法: " << generic_add(2.5, 3.7) << endl;    // 6.2
    cout << "string连接: " << generic_add(string("Hello"), string(" World")) << endl;
    
    // 2. 在STL算法中使用泛型Lambda
    vector<int> ints = {1, 2, 3};
    vector<double> doubles = {1.1, 2.2, 3.3};
    vector<string> strings = {"a", "b", "c"};
    
    auto print_vector = [](const auto& vec) {
        for (const auto& item : vec) {
            cout << item << " ";
        }
        cout << endl;
    };
    
    cout << "整数向量: ";
    print_vector(ints);
    
    cout << "浮点数向量: ";
    print_vector(doubles);
    
    cout << "字符串向量: ";
    print_vector(strings);
}

7. Lambda作为返回值和高阶函数

cpp

void higherOrderFunctions() {
    cout << "\n=== Lambda作为返回值和高阶函数 ===" << endl;
    
    // 1. 返回Lambda的函数
    auto create_multiplier = [](int factor) {
        return [factor](int value) {
            return value * factor;
        };
    };
    
    auto double_it = create_multiplier(2);
    auto triple_it = create_multiplier(3);
    
    cout << "10的双倍: " << double_it(10) << endl;   // 20
    cout << "10的三倍: " << triple_it(10) << endl;   // 30
    
    // 2. 接受Lambda作为参数的函数
    auto process_numbers = [](const vector<int>& nums, auto processor) {
        vector<int> result;
        for (int n : nums) {
            result.push_back(processor(n));
        }
        return result;
    };
    
    vector<int> numbers = {1, 2, 3, 4, 5};
    
    auto squared = process_numbers(numbers, [](int n) { return n * n; });
    auto incremented = process_numbers(numbers, [](int n) { return n + 10; });
    
    cout << "平方: ";
    for (int n : squared) cout << n << " ";
    cout << endl;
    
    cout << "加10: ";
    for (int n : incremented) cout << n << " ";
    cout << endl;
    
    // 3. 函数组合
    auto compose = [](auto f, auto g) {
        return [f, g](auto x) {
            return f(g(x));
        };
    };
    
    auto add_two = [](int x) { return x + 2; };
    auto times_three = [](int x) { return x * 3; };
    
    auto add_then_multiply = compose(times_three, add_two);
    auto multiply_then_add = compose(add_two, times_three);
    
    cout << "先加2再乘3: " << add_then_multiply(5) << endl;     // (5+2)*3 = 21
    cout << "先乘3再加2: " << multiply_then_add(5) << endl;     // (5*3)+2 = 17
}

8. Lambda的实际应用场景

场景1:回调函数

cpp

#include <functional>
#include <vector>

class Button {
private:
    string label;
    vector<function<void()>> click_handlers;
public:
    Button(string lbl) : label(lbl) {}
    
    void onClick(function<void()> handler) {
        click_handlers.push_back(handler);
    }
    
    void click() {
        cout << "按钮 '" << label << "' 被点击!" << endl;
        for (const auto& handler : click_handlers) {
            handler();
        }
    }
};

void callbackExample() {
    cout << "\n=== 回调函数场景 ===" << endl;
    
    Button btn("提交");
    int click_count = 0;
    
    // 使用Lambda作为回调
    btn.onClick([&click_count]() {
        click_count++;
        cout << "点击次数: " << click_count << endl;
    });
    
    btn.onClick([]() {
        cout << "执行提交操作..." << endl;
    });
    
    // 模拟点击
    btn.click();
    btn.click();
}

场景2:资源管理

cpp

#include <fstream>
#include <memory>

void resourceManagement() {
    cout << "\n=== 资源管理场景 ===" << endl;
    
    // 1. 使用Lambda进行RAII式资源管理
    auto file_processor = [](const string& filename) {
        ifstream file(filename);
        if (!file.is_open()) {
            return -1;
        }
        
        // Lambda确保文件在作用域结束时关闭
        return [file = move(file)]() mutable -> int {
            string line;
            int line_count = 0;
            while (getline(file, line)) {
                cout << "第" << ++line_count << "行: " << line << endl;
            }
            return line_count;
        };
    };
    
    // 2. 使用Lambda创建自定义删除器
    auto custom_deleter = [](FILE* file) {
        if (file) {
            fclose(file);
            cout << "文件已关闭" << endl;
        }
    };
    
    unique_ptr<FILE, decltype(custom_deleter)> file_ptr(
        fopen("test.txt", "w"), 
        custom_deleter
    );
    
    if (file_ptr) {
        fputs("Hello Lambda!", file_ptr.get());
    }
}

场景3:线程和异步编程

cpp

#include <thread>
#include <future>
#include <chrono>

void asyncProgramming() {
    cout << "\n=== 异步编程场景 ===" << endl;
    
    // 1. 在线程中使用Lambda
    vector<thread> threads;
    
    for (int i = 0; i < 3; ++i) {
        threads.emplace_back([i]() {
            this_thread::sleep_for(chrono::milliseconds(100 * i));
            cout << "线程 " << i << " 执行完成" << endl;
        });
    }
    
    for (auto& t : threads) {
        t.join();
    }
    
    // 2. 使用async和Lambda
    auto future1 = async(launch::async, []() {
        this_thread::sleep_for(chrono::seconds(1));
        return string("异步任务1完成");
    });
    
    auto future2 = async(launch::async, []() {
        this_thread::sleep_for(chrono::milliseconds(500));
        return string("异步任务2完成");
    });
    
    cout << future2.get() << endl;
    cout << future1.get() << endl;
}

9. Lambda的性能和最佳实践

cpp

void performanceAndBestPractices() {
    cout << "\n=== 性能考虑和最佳实践 ===" << endl;
    
    // 1. 避免不必要的捕获
    int important_data = 42;
    vector<int> large_data = {1, 2, 3, 4, 5};
    
    // 不好:捕获了整个large_data的拷贝
    // auto bad_lambda = [=]() { ... };
    
    // 好:只捕获需要的变量
    auto good_lambda = [&important_data, data_ref = ref(large_data)]() {
        cout << "重要数据: " << important_data << endl;
        // 使用data_ref而不是拷贝large_data
    };
    
    // 2. 使用constexpr Lambda (C++17)
    constexpr auto constexpr_lambda = [](int n) constexpr {
        return n * n;
    };
    
    constexpr int result = constexpr_lambda(5);  // 编译期计算
    cout << "编译期计算: " << result << endl;
    
    // 3. 立即调用Lambda
    int x = [](int a, int b) {
        return a + b;
    }(10, 20);  // 立即调用
    
    cout << "立即调用结果: " << x << endl;
    
    // 4. 使用std::function存储Lambda
    function<int(int, int)> func_store = [](int a, int b) {
        return a * b;
    };
    
    cout << "function存储: " << func_store(6, 7) << endl;
}

10. C++17/20中的Lambda新特性

cpp

void modernLambdaFeatures() {
    cout << "\n=== C++17/20 Lambda新特性 ===" << endl;
    
    // 1. constexpr Lambda (C++17)
    constexpr auto square = [](int n) constexpr {
        return n * n;
    };
    static_assert(square(5) == 25);
    
    // 2. 捕获*this (C++17)
    struct Processor {
        int value = 100;
        
        auto get_lambda() {
            // C++17前:捕获this指针
            // return [this]() { return value; };
            
            // C++17:捕获*this的拷贝
            return [*this]() mutable {
                value += 10;
                return value;
            };
        }
    };
    
    // 3. 模板Lambda (C++20)
    #if __cplusplus >= 202002L
    auto template_lambda = []<typename T>(T a, T b) {
        return a + b;
    };
    cout << "模板Lambda: " << template_lambda(5, 3) << endl;
    #endif
    
    // 4. 默认构造和赋值 (C++20)
    auto simple = [](int x) { return x * 2; };
    decltype(simple) simple_copy;  // C++20允许默认构造
    simple_copy = simple;          // C++20允许赋值
}

总结

Lambda表达式的核心优势:

  1. 简洁性:内联定义,代码更紧凑

  2. 灵活性:捕获外部变量,创建闭包

  3. 性能:通常可以被编译器内联优化

  4. 功能性:支持泛型、常量表达式等

关键语法要点:

  • [] 捕获列表:控制对外部变量的访问

  • () 参数列表:与普通函数相同

  • mutable:允许修改值捕获的变量

  • constexpr:编译期计算

  • -> return_type:显式指定返回类型

最佳实践:

  1. 尽量使用值捕获,避免意外的副作用

  2. 对于大型对象,使用引用捕获或初始化捕获

  3. 在性能关键路径考虑使用Lambda

  4. 合理使用泛型Lambda提高代码复用性

掌握Lambda表达式是现代C++编程的重要技能!

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐