C++11 Lambda表达式的本质是什么?它的捕获列表 ([]
) 是如何工作的?
Lambda表达式本质上是创建匿名函数对象(Functor) 的一种便捷语法糖。捕获列表 []
则决定了外部变量如何被这个函数对象"记住"
Lambda的本质:一个匿名的函数对象
下一个Lambda表达式时,编译器在底层会自动生成一个匿名的struct
或class
,并重载其函数调用运算符 operator()
eg:
c++
auto my_lambda = [](int x) { return x * 2; };
编译器在背后生成的代码,大致等价于下面这个函数对象:
c++
class __Lambda_Anonymous_Class__ {
public:
// 重载 operator(),使其行为与Lambda体一致
int operator()(int x) const {
return x * 2;
}
};
// 创建这个匿名类的实例
auto my_lambda = __Lambda_Anonymous_Class__{};
my_lambda(5)
实际上是调用了 __Lambda_Anonymous_Class__
实例的 operator()
方法。这就是Lambda表达式的本质
捕获列表 []
的工作原理
捕获列表是Lambda最强大的功能之一,它决定了Lambda如何"捕获"其所在作用域的外部变量。捕获的变量会成为上面那个匿名函数对象类的成员变量
按值捕获 [=]
或 [var]
:
按值捕获会将被捕获变量的一份拷贝存为匿名函数对象的成员变量
[=]
:捕获所有在Lambda体内用到的外部变量(按值)[var]
:只捕获名为var
的变量(按值)
c++
int main() {
int y = 10;
// 按值捕获 y
auto add_y = [y](int x) {
return x + y; // 这里的y是main函数里y的一份拷贝
};
std::cout << add_y(5) << std::endl; // 输出 15
y = 20; // 修改原始的y
std::cout << add_y(5) << std::endl; // 仍然输出 15,因为Lambda持有的是y=10时的拷贝
}
底层等价物:
c++
class __Add_Y_Functor__ {
private:
int y_copy_; // 按值捕获,成为成员变量
public:
__Add_Y_Functor__(int y_val) : y_copy_(y_val) {} // 构造时拷贝
int operator()(int x) const {
return x + y_copy_;
}
};
auto add_y = __Add_Y_Functor__(y); // y被拷贝到构造函数里
按引用捕获 [&]
或 [&var]
[&]
:捕获所有在Lambda体内用到的外部变量(按引用)[&var]
:只捕获名为var
的变量(按引用)
c++
int main() {
int y = 10;
// 按引用捕获 y
auto add_y_ref = [&y](int x) {
return x + y; // 这里的y是main函数里y的引用
};
std::cout << add_y_ref(5) << std::endl; // 输出 15
y = 20; // 修改原始的y
std::cout << add_y_ref(5) << std::endl; // 输出 25,因为Lambda持有的是y的引用
}
底层等价物:
c++
class __Add_Y_Ref_Functor__ {
private:
int& y_ref_; // 按引用捕获,成为引用成员
public:
__Add_Y_Ref_Functor__(int& y_val) : y_ref_(y_val) {}
int operator()(int x) const {
return x + y_ref_;
}
};
auto add_y_ref = __Add_Y_Ref_Functor__(y);
按引用捕获要非常小心,如果Lambda的生命周期超过了被引用变量的生命周期,会导致悬垂引用 (Dangling Reference)
mutable
关键字:
默认情况下,按值捕获的成员变量在Lambda体内是const
的,不能被修改。如果想修改这些拷贝,需要使用mutable
关键字。mutable
会移除operator()
的const
限定
c++
int counter = 0;
auto my_counter = [counter]() mutable {
counter++; // 正确!可以修改拷贝的counter
return counter;
};
std::cout << my_counter() << std::endl; // 输出 1
std::cout << my_counter() << std::endl; // 输出 2
std::cout << counter << std::endl; // 输出 0, 原始的counter未受影响