Lambda(λ):编译器自动生成的匿名仿函数的语法糖。可以就地定义可调用对象,能够捕获上下文中的局部变量。
写 lambda,编译器后台自动生成一个独一无二的类(仿函数),[]里捕获的变量变成这个类的成员变量;()是函数形参;{}是函数体;operator()被重载。

1. 为什么需要Lambda

1.1 没有Lambda痛点

  • 普通全局函数:无法直接访问局部变量,没有状态。
  • 仿函数struct Functor{ operator() }:要单独写结构体,代码割裂,定义位置离调用点很远。
  • std::bind:可以绑定参数,但语法晦涩,不支持直接捕获局部,复杂逻辑可读性差。
  • 函数指针:不能保存状态,仅无捕获 lambda 可以转为函数指针。

1.2 Lambda解决的问题

  • 就地定义匿名可调用对象,定义就在使用点附近,代码紧凑。
  • 捕获周围局部变量,直接使用当前作用域变量,不用手动传参。
  • 可以直接传给算法、std::function、std::thread、回调。
  • 不需要起函数名,用完即弃,适合一次性回调逻辑。
    注意:每一处 lambda,编译器都会生成一个全新的、唯一的匿名类型;不同 lambda 类型不相同,所以不能直接存容器,靠std::function做类型擦除统一保存。

2. 语法格式

[capture] (params) mutable noexcept -> return_type { body };

片段 名称 说明
[capture] 捕获子句,必须写 指定捕获哪些外部局部变量;[]不捕获任何变量
(params) 形参列表 和普通函数形参一样;C++14 支持 auto 泛型 lambda;无参数可以省略(除 mutable)
mutable 可变性修饰 默认 lambda 的operator()是 const,不能修改按值捕获的成员;mutable 去掉 const,可以修改值捕获副本
noexcept 异常说明 可选,标记不会抛异常
-> return_type 尾置返回类型,可选 大部分情况编译器自动推导返回值;多分支返回类型不一致时必须显式指定
函数体 函数逻辑

2.1 捕获列表 [] 语法

捕获写法 含义
[] 不捕获任何变量,只能使用全局 / 静态变量
[x] 值捕获,拷贝 x 到 lambda 内部;lambda 内是副本,外部原始变量不受修改;默认 const,不能改副本,需要mutable
[&x] 引用捕获,保存 x 的引用;直接操作外部原始变量;要警惕生命周期问题
[=] 值捕获全部用到的局部变量,自动拷贝,不捕获 this
[&] 引用捕获全部用到的局部变量,全部拿引用
[this] 捕获当前类的 this 指针,可以访问类成员变量、成员函数
[=, &a] 混合捕获:默认全部值捕获,唯独 a 使用引用捕获
[&, b] 混合捕获:默认全部引用捕获,唯独 b 使用值捕获
  • 值捕获示例:
int a = 10;
auto f = [a](){
    // a = 20; //编译报错!值捕获默认const operator()
    std::cout << a;
};
f();
  • mutable:修改值捕获的副本
int a = 10;
auto f = [a]() mutable {
    a = 99; //修改lambda内部副本,外部a不变
    std::cout << a;
};
f();
std::cout << a; //仍然是10
  • 引用捕获
int a = 10;
auto f = [&a](){
    a = 99; //直接修改外部原始变量
};
f();
std::cout << a; //输出99
  • this 捕获(类内部写 lambda)
struct Test{
    int val = 100;
    void run(){
        auto f = [this](){
            std::cout << val; //访问成员变量
        };
        f();
    }
};

2.2 返回类型推导

  • 函数体全部 return 返回同类型,编译器自动推导,可以省略-> type
  • 如果不同分支返回不同类型,必须显式尾置返回
//自动推导返回int
auto add = [](int x,int y){ return x+y; };

//必须显式写返回类型
auto fun = [](bool flag) -> double {
    if(flag) return 1.0;
    else return 2;
};
  • C++14 泛型 lambda,参数写 auto
auto glambda = [](auto a, auto b){ return a + b; };
glambda(1,2);
glambda(1.1, 2.2);

3. 应用场景

  • STL 算法就地回调
    std::sort、std::for_each、std::find_if,不用单独写函数 / 仿函数
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
    std::vector<int> v{3,1,4,2};
    std::sort(v.begin(),v.end(), [](int a,int b){ return a>b; });
    return 0;
}
  • std::thread 线程执行体
#include <thread>
int main()
{
    int x = 10;
    std::thread t([&x](){
        std::cout << x << std::endl;
    });
    t.join();
    return 0;
}
  • 存入std::function做回调、事件处理
#include <functional>
int main()
{
    std::function<void(int)> callback;
    int base = 100;
    callback = [base](int v){ std::cout << v + base; };
    callback(5);
    return 0;
}
  • 线程池 enqueue 传入任务
//直接传lambda给enqueue,内部会被std::bind包装成std::function<void()>
pool.enqueue([a,b](){
    std::cout << a+b;
});
  • std::bind 的替代品
//bind版本
auto f = std::bind(foo, std::placeholders::_2, std::placeholders::_1);
//等价lambda
auto f = [](auto x,auto y){ foo(y,x); };