一、概述
C++11 引入的 lambda 表达式是现代 C++ 中非常重要的特性,它简化了匿名函数的定义和使用,尤其在 STL 算法、回调函数等场景中广泛应用。
二、Lambda 表达式的基本语法
[capture-list](parameters) mutable -> return-type {
// 函数体
}
1. 捕获列表(Capture List)
- 用于捕获外部变量,控制 lambda 如何访问其作用域外的变量。
- 捕获方式:
- []:不捕获任何变量。
- [=]:隐式值捕获(所有外部变量以值传递)。
- [&]:隐式引用捕获(所有外部变量以引用传递)。
- [var]:显式值捕获单个变量。
- [&var]:显式引用捕获单个变量。
- 混合捕获:[=, &var] 或 [&, var]。
2. 参数列表(Parameters)
- 类似普通函数的参数列表。
- 无参数时可省略,如 []{}。
3. mutable 关键字
- 默认情况下,lambda 的 operator() 是 const 的,不能修改值捕获的变量。
- 使用 mutable 后,可以修改值捕获的变量(但修改不影响外部变量)。
4. 返回类型(Return Type)
- 可省略,编译器自动推导。
- 复杂逻辑需显式指定返回类型,如 -> int。
三、Lambda 的实现原理
在 C++11 中,Lambda 表达式的底层实现原理本质上是 “匿名仿函数(函数对象)”,即编译器会将 Lambda 转换为一个 匿名类,然后用该类的对象来调用 operator()。这个类可能会存储捕获的变量,并根据不同的捕获方式(值捕获、引用捕获等)来决定它的成员变量和 operator() 方法的行为。
Lambda 转换为匿名类:
示例 Lambda 表达式
auto lambda = [](int x) { return x + 1; };
等价转换的 C++ 代码 编译器会生成一个类似如下的匿名类:
class LambdaClass {
int operator()(int x) const {
return x + 1;
}
};
然后 lambda 变量实际上是该类的一个实例:
LambdaClass lambda;
int result = lambda(10); // 等价于 lambda.operator()(10)
四、Lambda 捕获变量的实现
如果 Lambda 需要捕获变量,编译器会在生成的匿名类中存储这些变量,并在 operator() 中使用它们。
1.值捕获([a])
int a = 10;
auto lambda = [a]() { return a + 1; };
编译器生成的类:
class LambdaClass {
int a; // 复制 a 的值
LambdaClass(int a_) : a(a_) {} // 通过构造函数初始化
int operator()() const {
return a + 1;
}
};
实际调用:
LambdaClass lambda(10);
int result = lambda(); // 11
这里 a 被复制到 LambdaClass 中,因此即使 a 的原始变量作用域结束,该 Lambda 仍然可以访问 a 的值。
2.引用捕获([&a])
int a = 10;
auto lambda = [&a]() { return a + 1; };
编译器生成的类:
class LambdaClass {
int& a; // 引用 a
LambdaClass(int& a_) : a(a_) {} // 通过引用初始化
int operator()() const {
return a + 1;
}
};
实际调用:
a = 20;
LambdaClass lambda(a);
int result = lambda(); // 21
因为 a 是按引用捕获的,lambda() 调用时会访问 a 的最新值。
3.隐式捕获 [=] 和 [&]
如果使用 [=],所有变量都会按值捕获;如果使用 [&],所有变量都会按引用捕获:
int x = 5, y = 10;
auto lambda1 = [=]() { return x + y; }; // 值捕获
auto lambda2 = [&]() { return x + y; }; // 引用捕获
其底层实现类似于:
class LambdaClass1 {
int x, y;
LambdaClass1(int x_, int y_) : x(x_), y(y_) {}
int operator()() const { return x + y; }
};
class LambdaClass2 {
int &x, &y;
LambdaClass2(int& x_, int& y_) : x(x_), y(y_) {}
int operator()() const { return x + y; }
};
4.Lambda 的 mutable 关键字
默认情况下,Lambda 的 operator() 是 const,即不能修改值捕获的变量:
int a = 10;
auto lambda = [a]() mutable { a += 5; return a; };
编译器生成的类:
class LambdaClass {
int a;
LambdaClass(int a_) : a(a_) {}
int operator()() { // 不是 const 版本
a += 5;
return a;
}
};
注意:mutable 允许修改值捕获的变量,但不会影响外部变量。
5.Lambda 的 std::function 适配
Lambda 也可以转换为 std::function,但会涉及类型擦除:
#include <functional>
#include <iostream>
int main() {
int a = 10;
std::function<int()> f = [a]() { return a + 1; }; // Lambda 转换为 std::function
std::cout << f() << std::endl; // 输出 11
}
此时 std::function 内部会存储一个 动态分配的 Lambda 对象,增加了一定的开销。
6.Lambda 是什么类型?
Lambda 不能直接获取类型,但可以使用 decltype 推导:
auto lambda = [](int x) { return x + 1; };
using LambdaType = decltype(lambda);
或者 typeid:
#include <typeinfo>
#include <iostream>
int main() {
auto lambda = [](int x) { return x + 1; };
std::cout << typeid(lambda).name() << std::endl;
}
每个 Lambda 都有一个 独特的类型,但都可以自动转换为 函数指针(如果没有捕获变量):
using FuncPtr = int(*)(int);
FuncPtr func = [](int x) { return x + 1; };
五、总结
- Lambda 本质上是 匿名类,其 operator() 方法定义 Lambda 逻辑。
- 如果有捕获,编译器会将捕获的变量存储为类的成员变量。
- mutable 允许修改值捕获的变量,但不会影响外部变量。
- 无捕获的 Lambda 可以转换为函数指针,有捕获的 Lambda 需要使用 std::function。