c++ lambda表达式用来创建一个闭包,闭包是一种可以捕获作用域内(闭包所在的代码块)的变量的一个匿名的函数对象。

labmda表达式结构:

[捕获列表](参数列表) mutable 异常属性 -> 返回类型 { 函数体 }

其中:

  • 捕获列表:指定lambda 表达式可以访问的外部变量,以及访问方式(按值或按引用)。
  • 参数列表:定义lambda 函数体的形参列表。
  • mutable:(可选) 允许修改按值捕获的变量,默认情况下,按值捕获的变量是const的。
  • 异常属性:(可选) 声明lambda 表达式可能抛出的异常类型。
  • -> 返回类型:(可选) 指定lambda 表达式的返回类型,如果省略,编译器会自动推断(如果省略返回类型,也必须省略 ->)。
  • 函数体:lambda 表达式的具体实现,与普通函数相同。

捕获列表

捕获列表的形式:

  • []: 不捕获任何外部变量。
  • [=]: 按值捕获所有外部变量。
  • [&]: 按引用捕获所有外部变量。
  • [x, &y]: 按值捕获变量 x,按引用捕获变量 y。
  • [=, &z]: 按值捕获所有外部变量,但按引用捕获变量 z。
  • [&, x]: 按引用捕获所有外部变量,但按值捕获变量 x。
  • [this]: 捕获当前类的this指针。

mutable

mutable关键字指示函数体中允许修改值捕获的变量,但该变量只在函数体中生效,不会影响到外部变量的值,因为值传递是以拷贝的方式实现的。

#include <iostream>

int main()
{
    int i = 1;
    auto func = [=] () mutable {
        i++; // 不加mutable就修改i的值会导致编译报错,因为捕捉值时i是const常量。
        std::cout << "i = " << i << endl; // 2
    };
    func();
    std::cout << "i = " << i << endl;    // 1
}

lambda表达式的返回类型

  • auto: 最简单,最推荐,编译器自行推断。
  • std::function<返回值类型(参数类型列表)>: 相比于auto,可能会有一些微小的性能开销,std::function是一个模板类。

lambda的工作机制

编译器会为每个lambda函数生成一个唯一闭包。

[&i] (int j) { std::cout << i << j << std::endl; }

// 等价于

struct anonymous {
    int &m_i;
    anonymous(int &i) : m_i(i) {}
    inline auto operator()(int j) const {
        std::cout << m_i << j << std::endl;
    }
};

C++14中的变化

参数列表中可以使用auto关键字

auto generic_lambda = [](auto a, auto b) {
    return a + b;
};
std::cout << generic_lambda(1, 2) << std::endl;      // int
std::cout << generic_lambda(1.5, 2.5) << std::endl;  // double

捕获列表中可以使用 [=][&] 之外的方式来创建新的变量

std::unique_ptr<int> p(new int(10));
auto my_lambda = [my_p = std::move(p)]() {
    std::cout << *my_p << std::endl;
};
my_lambda();

带初始化的捕获列表

int m = 3;
auto f1 = [&n = m] () {
    n++;
    std::cout << n << std::endl; // 4
};
f1();

auto f2 = [n = m] () mutable {
    n++;
    std::cout << n << std::endl; // 5
};
f2();

std::cout << m << std::endl; // 4

C++17中的变化

  1. *this按值捕获

    允许你按值捕获整个类对象,得到一个它所属类对象的拷贝。

  2. constexpr lambda表达式

int y = 32;
auto answer = [y]() constexpr {
    int x = 10;
    return y + x;
};

C++20中的变化

lambda支持模板参数

auto lambda = []<typename T>(std::vector<T>& vec) {
    // ...
};