lambda表达式
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中的变化
-
*this按值捕获允许你按值捕获整个类对象,得到一个它所属类对象的拷贝。
-
constexprlambda表达式
int y = 32;
auto answer = [y]() constexpr {
int x = 10;
return y + x;
};
C++20中的变化
lambda支持模板参数
auto lambda = []<typename T>(std::vector<T>& vec) {
// ...
};