如何看懂Lambda表达式
下面给出一种「由外到内、由简到繁」的分层方法,帮助你把 C++ Lambda 函数“一眼看穿”。读完之后,再遇到形如
auto f = [capture](params) specifiers -> return_type { body };
的代码时,可以像读普通函数一样迅速拆解。
一、先把它当成“匿名 struct + operator()”
编译器看见 Lambda 时,会偷偷生成一个未命名的类(闭包类型),大致如下:
struct __lambda_123 {
/* 捕获的成员变量 */
auto operator()(params) const specifiers -> return_type { body }
};
有了这个映射,你就不会觉得 Lambda 是什么“黑魔法”——它就是把函数体搬到类里,再给你个对象而已。
二、4 个语法部件快速定位
-
捕获列表
[...]
决定“把哪些外部变量搬进闭包对象”。[]什么都不捕获[=]全部变量值捕获(按值复制)[&]全部变量引用捕获[x, &y]x 按值,y 按引用[this]捕获当前对象的指针[val = expr]C++14 起支持“初始化捕获”,相当于成员变量 val(expr)
-
形参列表
(...)
和普通函数一模一样,可省略(无参时)。 -
后置修饰
specifiers(可选)
常见组合:mutable允许修改按值捕获的副本(默认 operator() 是 const)constexpr / constevalnoexcept-> return_type尾返回类型,用于推导不了或想显式写时- C++20 起可加模板形参、约束、协程等。
-
函数体
{ ... }
内部代码可直接使用捕获的成员变量(名字与外部变量相同)。
三、一个例子逐层解剖
std::vector<int> v = {1,2,3,4};
int factor = 2;
std::for_each(v.begin(), v.end(),
[&, factor](int& x) mutable noexcept {
x += factor++;
});
拆解:
-
捕获
[&, factor]&表示其余变量(v 等)按引用捕获;- 单独列出
factor,强制按值捕获(即闭包内保存一个副本)。
因此闭包里相当于有
struct __lambda { int factor; // 按值 std::vector<int>& v; // 按引用 }; -
形参
(int& x)
每次算法传入 vector 元素的引用。 -
修饰
mutable noexceptmutable允许修改factor副本;noexcept说明不会抛异常。
-
函数体
x += factor++;
把factor的副本累加到元素上,然后自增副本(不会影响外部factor)。
四、常见疑问 5 连答
-
为什么
[=]里修改变量,外部没变?
因为闭包里存的是“副本”,除非用mutable,否则连副本都改不了。 -
想捕获 move-only 类型(如 unique_ptr)怎么办?
C++14 起用初始化捕获:[p = std::move(ptr)]{ ... }。 -
Lambda 能递归吗?
C++11 不行;C++14 起auto f = [](auto self, int n){ return n<2?1:n*self(self,n-1); };
或把闭包对象先存到std::function里再递归。 -
如何返回另一个 Lambda?
直接写即可,编译器会推导出闭包类型;若想类型擦除,用std::function<...>包装。 -
模板 Lambda 怎么用?
C++20 起:[]<typename T>(T a, T b){ return a + b; }
五、记忆口诀(助记版)
“捕参饰体,尾返可省;
按值只读,欲改加 mut;
初始捕获,move 无忧;
看作匿名类,万事不愁。”
结语
把 Lambda 当成“语法糖形式的函数对象”,再按上面 4 个部件扫一眼,任何复杂 Lambda 都能迅速拆分。写代码时,先想清楚“我要捕获哪些变量、按值还是引用、是否需要 mutable”,随后填形参和函数体即可。
更多推荐
所有评论(0)