一、lambda 表达式语法

1.1 基本格式

组成部分 说明 是否可省略
[capture-list] 捕捉列表:捕获上下文变量 ❌ 不可省略
(parameters) 参数列表:同普通函数 ✅ 无参数可省略
-> return_type 返回值类型:追踪返回类型 ✅ 可推导时省略
{ function_body } 函数体:实现逻辑 ❌ 不可省略

1.2 语法规则详解

规则 1:捕捉列表不可省略

规则 2:参数列表可省略

规则 3:返回值可省略(编译器推导)

规则 4:函数体不可省略

1.3 完整示例

int main() {
    // 最简单的 lambda
    auto add1 = [](int x, int y) -> int { return x + y; };
    cout << add1(1, 2) << endl;  // 输出 3

    // 省略参数列表(无参数)
    auto func1 = [] {
        cout << "hello bit" << endl;
        return 0;
    };
    func1();

    // 省略返回值
    auto func2 = [](int x, int y) { return x + y; };

    // 交换两个变量
    int a = 0, b = 1;
    auto swap1 = [](int& x, int& y) {
        int tmp = x;
        x = y;
        y = tmp;
    };
    swap1(a, b);
    cout << a << ":" << b << endl;  // 输出 1:0

    return 0;
}

二、捕捉列表

lambda 表达式中默认只能使用 lambda 函数体和参数中的变量。如果想用外层作用域中的变量,就需要进行捕捉。

2.1 显式捕捉

传值捕捉

int a = 0, b = 1, c = 2, d = 3;

auto func1 = [a, &b] {        // a 值捕捉,b 引用捕捉
    // a++;                     // ❌ 错误!值捕捉默认是 const 的,不能修改
    b++;                        // ✅ 正确!引用捕捉可以修改
    int ret = a + b;
    return ret;
};
cout << func1() << endl;        // 输出 2(a=0, b=2)

传引用捕捉

auto func2 = [&a, &b, &c, &d] {  // 全部引用捕捉
    a++;
    b++;
    c++;
    d++;
    return a + b + c + d;
};
func2();
cout << a << " " << b << " " << c << " " << d << endl;  // 输出 1 2 3 4

2.2 隐式捕捉

隐式值捕捉(=)

int a = 0, b = 1, c = 2;

auto func2 = [=] {              // = 表示隐式值捕捉:用了哪些就捕捉哪些
    int ret = a + b + c;        // 编译器自动捕捉 a, b, c
    return ret;
};
cout << func2() << endl;        // 输出 3

隐式引用捕捉(&)

auto func3 = [&] {               // & 表示隐式引用捕捉
    a++;                        // 修改外部变量
    c++;
    d++;
};
func3();
cout << a << " " << b << " " << c << " " << d << endl;  // 输出 1 1 3 4

2.3 混合捕捉

语法规则

  • 第一个元素必须是 &=

  • & 混合捕捉时,后面的变量必须是值捕捉

  • = 混合捕捉时,后面的变量必须是引用捕捉

2.4 mutable 关键字

问题:值捕捉默认是 const 的

解决:mutable 取消常量性

重要

  • mutable 相当于去掉 const 属性

  • 但修改的是形参副本不会影响外部变量

  • 使用 mutable 后,参数列表不可省略(即使参数为空)

2.5 捕捉规则总结

捕捉方式 语法 能否修改外部变量 说明
显式值捕捉 [x, y] 拷贝外部变量,默认 const
显式引用捕捉 [&x, &y] 引用外部变量
隐式值捕捉 [=] 自动值捕捉使用的变量
隐式引用捕捉 [&] 自动引用捕捉使用的变量
混合捕捉 [&, x] x❌ 其他✅ 其他引用,x 值捕捉
混合捕捉 [=, &x] x✅ 其他❌ 其他值,x 引用捕捉
mutable [=]() mutable 形参可改 取消 const,但不影响实参

2.6 特殊规则

全局变量和静态变量不需要捕捉

只能捕捉 lambda 位置之前定义的变量


三、lambda 的应用

3.1 替代仿函数

在学习 lambda 之前,可调用对象只有函数指针仿函数对象

可调用对象 优点 缺点
函数指针 简单 类型定义麻烦,无法捕获上下文
仿函数 可捕获状态(成员变量) 需要定义一个类,代码冗余
lambda 简洁 + 可捕获状态

仿函数写法(繁琐)

struct ComparePriceLess {
    bool operator()(const Goods& gl, const Goods& gr) {
        return gl._price < gr._price;
    }
};

struct ComparePriceGreater {
    bool operator()(const Goods& gl, const Goods& gr) {
        return gl._price > gr._price;
    }
};

struct CompareEvaluateLess {
    bool operator()(const Goods& gl, const Goods& gr) {
        return gl._evaluate < gr._evaluate;
    }
};

// 使用
sort(v.begin(), v.end(), ComparePriceLess());
sort(v.begin(), v.end(), ComparePriceGreater());

lambda 写法(简洁)

sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
    return g1._price < g2._price;
});

sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
    return g1._price > g2._price;
});

sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
    return g1._evaluate < g2._evaluate;
});

3.2 排序比较器

struct Goods {
    string _name;       // 名字
    double _price;      // 价格
    int _evaluate;      // 评价

    Goods(const char* str, double price, int evaluate)
        : _name(str), _price(price), _evaluate(evaluate) {}
};

int main() {
    vector<Goods> v = {
        {"苹果", 2.1, 5},
        {"香蕉", 3, 4},
        {"橙子", 2.2, 3},
        {"菠萝", 1.5, 4}
    };

    // 按价格升序
    sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
        return g1._price < g2._price;
    });

    // 按价格降序
    sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
        return g1._price > g2._price;
    });

    // 按评价升序
    sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
        return g1._evaluate < g2._evaluate;
    });

    // 按评价降序
    sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
        return g1._evaluate > g2._evaluate;
    });

    return 0;
}

3.3 线程执行逻辑

#include <thread>

int main() {
    int id = 42;

    // lambda 作为线程执行函数
    thread t([id] {                 // 值捕捉 id
        cout << "Thread " << id << " is running" << endl;
    });

    t.join();
    return 0;
}

3.4 智能指针定制删除器

#include <memory>

// 自定义删除器:用 lambda 替代函数对象
auto fileDeleter = [](FILE* fp) {
    if (fp) {
        fclose(fp);
        cout << "File closed" << endl;
    }
};

int main() {
    // unique_ptr 使用 lambda 删除器
    unique_ptr<FILE, decltype(fileDeleter)> fp(
        fopen("test.txt", "r"), 
        fileDeleter
    );

    // shared_ptr 也可以
    shared_ptr<FILE> sp(
        fopen("test.txt", "r"),
        [](FILE* fp) { fclose(fp); }
    );

    return 0;
}


四、lambda 的原理

4.1 编译器生成的仿函数类

lambda 的原理和范围 for 很像:

编译后从汇编指令层看,压根就没有 lambda 这种东西。

lambda 的底层是仿函数对象编译器会生成一个对应的仿函数类。

// ========== 仿函数写法 ==========
class Rate {
public:
    Rate(double rate) : _rate(rate) {}

    double operator()(double money, int year) {
        return money * _rate * year;
    }

private:
    double _rate;
};

// ========== 等价的 lambda 写法 ==========
double rate = 0.49;
auto r2 = [rate](double money, int year) {
    return money * rate * year;
};

编译器生成的类(伪代码)

// 编译器为 lambda 生成的类(类名是编译器按规则生成的)
class __lambda_xxx {                // 类名保证唯一性
public:
    // 构造函数:用捕捉列表初始化成员变量
    __lambda_xxx(double rate) : _rate(rate) {}

    // operator():参数和返回值来自 lambda 定义
    double operator()(double money, int year) const {
        return money * _rate * year;
    }

private:
    double _rate;                    // 捕捉列表的变量 → 成员变量
};

// 编译器将 lambda 替换为:
// auto r2 = __lambda_xxx(rate);

捕捉列表的本质

lambda 语法 编译器实现
[rate] double _rate; 成员变量
[&rate] double& _rate; 成员引用
[=] 自动生成的多个成员变量
[&] 自动生成的多个成员

更多推荐