C++ Lambda 表达式

C++ Lambda 表达式

1. Lambda 表达式概述

Lambda 表达式是 C++11 引入并在后续版本(C++14、C++17、C++20)中持续优化的语法糖,它提供了一种在调用或传参位置定义匿名函数对象的便捷方式。这种特性特别适合封装短小的代码片段,常用于传递给算法函数或异步方法,在现代 C++ 开发及开源项目中应用广泛。

2. Lambda 表达式定义

2.1 Lambda 表达式示例

以下是一个在 vector 遍历中使用 Lambda 的简单示例,展示了无需预先定义函数即可实现打印功能:

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

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5};
    // 使用Lambda表达式作为for_each的第三个参数
    std::for_each(nums.begin(), nums.end(),
        [](int num) {
            std::cout << num << " ";
        }
    );
    return 0;
}

该示例中,Lambda 表达式直接替代了传统的函数定义,简化了代码结构,对于仅需一次使用的简单功能,显著提升了代码的紧凑性。

2.2 Lambda 表达式语法定义

Lambda 表达式的完整语法结构如下:

[捕获列表] (参数列表) [mutable] [异常说明] -> 返回类型 { 函数体 }
  • 捕获列表:位于 Lambda 起始位置,用[]表示,用于指定可访问的上下文变量及其访问方式(值捕获或引用捕获)。

  • 参数列表:与普通函数的参数列表一致,无参数时可省略括号(但有 mutable 时不可省略)。

  • mutable:可选修饰符,用于取消 Lambda 的常量性,允许修改值捕获的变量,使用时必须带参数列表(即使为空)。

  • 异常说明:如throw(),表示 Lambda 不抛出异常,C++11 后已弃用,不建议使用。

  • 返回类型:通过-> 返回类型指定,可省略让编译器自动推导(单 return 语句或无返回值时通常省略)。

  • 函数体:包含具体执行逻辑,可使用参数和捕获的变量。

2.3 Lambda 表达式参数详解

2.3.1 捕获列表

捕获列表是 Lambda 与普通函数的核心区别,支持多种捕获方式:

  • []:不捕获任何变量。
auto func = []{ std::cout << "无捕获变量"; };
  • [var]:值捕获变量 var,Lambda 内使用 var 的副本。
int a = 10;
auto func = [a]{ std::cout << a; }; // 输出10
  • [&var]:引用捕获变量 var,Lambda 内可修改原变量。
int b = 20;
auto func = [&b]{ b = 30; };
func(); // 执行后b变为30
  • [=]:值捕获所有上下文变量(包括 this 指针)。
int x = 5, y = 6;
auto func = [=]{ return x + y; }; // 返回11
  • [&]:引用捕获所有上下文变量(包括 this 指针)。
int m = 1, n = 2;
auto func = [&]{ m = 3; n = 4; };
func(); // 执行后m=3,n=4
  • [this]:值捕获当前对象的 this 指针,用于访问类成员。
class Test {
public:
    void print() { std::cout << "Test"; }
    void useLambda() {
        auto func = [this]{ print(); }; // 调用类成员函数
        func();
    }
};
  • 混合捕获:如[=, &var](值捕获所有变量,引用捕获 var)、[&, var](引用捕获所有变量,值捕获 var)。
int p = 100, q = 200;
auto func = [=, &p]{ p = 300; return q; }; // p引用捕获,q值捕获

注意:不可重复捕获(如[=, a]为错误,因=已包含 a 的 值捕获)。

2.3.2 参数列表

与普通函数参数列表用法一致,支持各种类型参数。

auto add = [](int a, double b){ return a + b; };
std::cout << add(2, 3.5); // 输出5.5

2.3.3 mutable 修饰符

取消 Lambda 的常量性,允许修改值捕获的变量。

int c = 5;
auto func = [c]() mutable { c = 10; std::cout << c; }; // 输出10
func();
std::cout << c; // 仍为5(值捕获的是副本)

2.3.4 返回类型

可省略让编译器推导,复杂场景需显式指定。

auto func1 = [](int x){ return x * 2; }; // 自动推导返回int
auto func2 = [](int x, int y) -> double { return x / y; }; // 显式返回double

3. Lambda 表达式的优缺点

优点

  • 无需预先定义函数,在调用位置直接实现逻辑,简化代码结构。

  • 对于短小逻辑,避免函数名冗余,提升代码可读性(如 STL 算法参数)。

缺点

  • 语法灵活导致复杂 Lambda 难以理解,增加代码维护成本。

  • 匿名特性使其无法复用,适合仅使用一次的场景。

4. Lambda 表达式工作原理

编译器会将 Lambda 表达式转换为匿名类( functor,函数对象),并重载operator()方法。例如:

auto print = []{ std::cout << "Hello"; };

等价于:

class AnonymousClass {
public:
    void operator()() const {
        std::cout << "Hello";
    }
};
auto print = AnonymousClass();

这种机制使得 Lambda 能像普通函数对象一样被传递和调用。

5. Lambda 表达式适用场景

5.1 STL 算法库

在排序、查找等算法中定义临时逻辑。

std::vector<int> nums = {3, 1, 4, 1, 5};
// 按绝对值排序
std::sort(nums.begin(), nums.end(), [](int a, int b){
    return std::abs(a) < std::abs(b);
});

5.2 短小逻辑场景

替代仅使用一次的简单函数。

// 过滤并打印偶数
std::vector<int> data = {1,2,3,4,5,6};
std::for_each(data.begin(), data.end(), [](int num){
    if (num % 2 == 0) std::cout << num << " ";
});

5.3 多线程编程

定义线程执行逻辑或同步条件。

#include <thread>
#include <mutex>

std::mutex mtx;
int count = 0;

int main() {
    std::thread t([]{
        std::lock_guard<std::mutex> lock(mtx);
        count++;
    });
    t.join();
    return 0;
}

5.4 函数对象与回调

结合std::function实现回调机制。

#include <functional>

void process(std::function<void(int)> callback) {
    callback(100);
}

int main() {
    process([](int val){ std::cout << "接收值:" << val; });
    return 0;
}

5.5 GUI 编程(如 Qt)

简化信号与槽的连接逻辑。

#include <QPushButton>

QPushButton* btn = new QPushButton("点击");
connect(btn, &QPushButton::clicked, []{
    std::cout << "按钮被点击";
});

6. 总结

Lambda 表达式作为现代 C++ 的重要特性,以其便捷性和灵活性极大简化了代码编写,尤其在 STL 算法、多线程和回调场景中优势显著。尽管存在语法复杂度的问题,但掌握其使用方法对于理解和编写现代 C++ 代码至关重要,是每位 C++ 开发者必备的技能。

posted @ 2025-07-30 00:40  Zyihan_Crz  阅读(53)  评论(0)    收藏  举报