Lambda 表达式是 C++11 及后续标准引入的重要特性,它允许我们定义匿名函数,极大地提升了代码的简洁性和灵活性。本文将结合实战代码,从基础语法、捕获方式、mutable 关键字、结合 STL / 智能指针使用等维度,全面解析 Lambda 表达式的核心知识点。

一、Lambda 表达式基础语法

Lambda 表达式的核心语法结构如下:

[capture](parameters) mutable -> return_type { function_body }
  • 捕获子句(capture):指定外部变量如何被 Lambda 访问(值捕获、引用捕获等);
  • 参数列表(parameters):与普通函数参数列表一致,C++14 支持自动类型推导(auto);
  • mutable:可选,移除 Lambda 体内变量的 const 限制(仅对值捕获生效);
  • 返回类型(return_type):可选,编译器可自动推导;
  • 函数体(function_body):Lambda 的执行逻辑。

最简示例

#include<iostream>
using namespace std;

int main() {
    // 无捕获、无参数、返回值自动推导
    auto add = [](int a, int b) { return a + b; };
    cout << "10 + 20 = " << add(10, 20) << endl; // 输出30

    // C++14自动类型推导参数
    auto print = [](auto x) { cout << "值:" << x << endl; };
    print(123);   // 输出整数
    print(3.14);  // 输出浮点数
    return 0;
}

二、Lambda 的变量捕获规则

捕获子句是 Lambda 的核心,决定了如何访问外部作用域的变量,主要分为以下几种:

1. 值捕获([=] 或 显式指定变量)

值捕获会拷贝外部变量到 Lambda 内部,默认情况下 Lambda 体内的拷贝变量是const的(不可修改)。

#include<iostream>
using namespace std;

class Int {
private:
    int value;
public:
    Int(int x = 0) : value(x) {}
    void Print() const { cout << "value: " << value << endl; }
    int& Value() { return value; }
};

void func(int a) {
    Int it(a);
    // [=] 捕获所有外部变量(值拷贝),mutable 解除const限制
    auto lambda = [=](int x) mutable -> void {
        it.Value() = 100; // 需mutable才能修改拷贝的变量
        it.Print();       // 输出value: 100
        a = 200;          // 修改的是拷贝的a,不影响外部
        cout << "内部a: " << a << endl; // 输出200
    };
    lambda(0);
    cout << "外部a: " << a << endl; // 输出原始值(未被修改)
}

int main() {
    func(10);
    return 0;
}

注意:值捕获的变量是 Lambda 创建时的拷贝,而非执行时的最新值。

2. 引用捕获([&] 或 显式指定变量)

引用捕获直接访问外部变量的内存地址,修改会影响原变量,无需 mutable 即可修改。

#include<iostream>
using namespace std;

class Int {
private:
    int value;
public:
    Int(int x = 0) : value(x) {}
    void Print() const { cout << "value: " << value << endl; }
    void setValue(int x) { value = x; }
};

int main() {
    Int a(10);
    a.Print(); // 输出value: 10
    
    // [&] 引用捕获所有外部变量
    auto lambda = [&]() { a.setValue(100); };
    lambda();
    a.Print(); // 输出value: 100(原变量被修改)
    return 0;
}

3. 混合捕获

可混合使用值捕获和引用捕获,显式指定部分变量的捕获方式:

void func(int a, int b) {
    Int ita(a), itb(b);
    // 引用捕获ita,值捕获其他所有变量
    auto lambda = [=, &ita]() -> void {
        ita.setValue(100); // 引用捕获,修改原变量
        itb.Print();       // 值捕获,访问拷贝的itb
        cout << a << endl; // 值捕获,访问拷贝的a
    };
    lambda();
}

4. this 捕获(类成员函数中)

在类的成员函数中,[this] 可捕获当前对象的 this 指针,从而访问类的成员变量 / 成员函数:

#include<iostream>
using namespace std;

class Int {
private:
    int value;
    static int num;
public:
    Int(int x = 0) : value(x) {}
    void func() {
        // 捕获this指针,访问成员变量和静态成员
        auto lambda = [this]() -> void {
            value = 1000;       // 等价于this->value
            num += 100;         // 静态成员可直接访问
            cout << value << endl; // 输出1000
            cout << num << endl;   // 输出100
        };
        lambda();
    }
    void Print() const { cout << "value: " << value << endl; }
};
int Int::num = 0;

int main() {
    Int a(10);
    a.func();
    a.Print(); // 输出value: 1000
    return 0;
}

三、Lambda 结合智能指针使用

Lambda 表达式常与智能指针(如shared_ptr)结合,需注意捕获方式对引用计数的影响:

#include<iostream>
#include<memory>
using namespace std;

class Int {
private:
    int value;
public:
    Int(int x = 0) : value(x) {
        cout << "创建对象:" << value << endl;
    }
    ~Int() {
        cout << "销毁对象:" << value << endl;
    }
    void Print() const { cout << "value: " << value << endl; }
    int& Value() { return value; }
};

int main() {
    shared_ptr<Int> pa(new Int(10));
    cout << "初始引用计数:" << pa.use_count() << endl; // 输出1

    // 值捕获shared_ptr,引用计数+1
    auto lambda = [pa]() -> void {
        pa->Print();          // 输出value: 10
        pa->Value() = 100;
        pa->Print();          // 输出value: 100
        cout << "Lambda内引用计数:" << pa.use_count() << endl; // 输出2
    };
    lambda();
    pa->Print(); // 输出value: 100(原对象被修改)
    cout << "Lambda执行后引用计数:" << pa.use_count() << endl; // 输出1
    return 0;
}

说明:值捕获shared_ptr会增加引用计数,确保 Lambda 执行期间对象不会被销毁;执行完毕后 Lambda 销毁,引用计数减 1。

四、Lambda 的实战场景:线程同步(条件变量)

Lambda 表达式可简化条件变量的谓词判断,让代码更简洁:

#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>
using namespace std;

const int n = 10;
int tag = 0;
mutex mtx;
condition_variable cond;

// 线程1:输出偶数
void funa() {
    unique_lock<mutex> locker(mtx);
    for (int i = 0; i <= n; ++i) {
        // Lambda作为谓词,判断tag是否为偶数
        cond.wait(locker, []() -> bool { return tag % 2 == 0; });
        if (tag <= n) {
            printf("funa: %d\n", tag);
        }
        tag += 1;
        cond.notify_all(); // 唤醒其他线程
    }
}

// 线程2:输出奇数
void funb() {
    unique_lock<mutex> locker(mtx);
    for (int i = 0; i <= n; ++i) {
        cond.wait(locker, []() -> bool { return tag % 2 == 1; });
        if (tag <= n) {
            printf("funb: %d\n", tag);
        }
        tag += 1;
        cond.notify_all();
    }
}

int main() {
    thread tha(funa);
    thread thb(funb);
    tha.join();
    thb.join();
    return 0;
}

上述代码通过 Lambda 作为condition_variable::wait的谓词,替代了传统的 while 循环判断,代码更简洁易读。

五、Lambda 表达式的核心总结

  1. 匿名性:Lambda 是匿名函数,可通过 auto 接收其类型,直接调用或作为参数传递;
  2. 捕获规则:
    • []:无捕获;
    • [=]:值捕获所有外部变量;
    • [&]:引用捕获所有外部变量;
    • [var]:值捕获指定变量 var;
    • [&var]:引用捕获指定变量 var;
    • [this]:类成员函数中捕获 this 指针;
  3. mutable:仅对值捕获生效,解除 Lambda 体内拷贝变量的 const 限制;
  4. 灵活性:C++14 支持自动类型推导参数,可适配不同类型的输入;
  5. 生命周期:值捕获的变量是拷贝,引用捕获需注意外部变量的生命周期(避免悬空引用)。

        Lambda 表达式是现代 C++ 的核心特性之一,熟练掌握其用法可大幅提升代码的简洁性和可读性,尤其在 STL 算法、线程编程、回调函数等场景中应用广泛。希望本文的实战案例能帮助大家深入理解 Lambda 的本质和用法。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐