Lambda
基本概念
定义
Lambda函数(亦称为Lambda表达式、匿名函数)是一种函数对象(变量),可以让函数像普通变量一样进行赋值、传递、函数返回等操作。
核心优势
- 简化程序代码:针对一次性使用的函数,无需定义命名的普通函数,减少代码冗余。
- 支撑函数式编程:让函数具备“可流动”特性,为C++函数式编程模式奠定语法基础。
语法逻辑
完整语法结构
Lambda表达式的完整语法格式如下:
[capture list](params list) mutable exception -> return type { function body }
各组成部分解析
| 组成部分 | 含义 & 注意事项 |
|---|---|
| capture list | 外部变量捕获列表,指定Lambda可访问的外部作用域变量,是Lambda的核心特征之一。 |
| params list | 形参列表,与普通函数形参列表语法一致;使用mutable时该列表不可省略。 |
| mutable | 取消Lambda的常量性,允许在函数体内修改值捕获的外部变量;使用时形参列表不可省略。 |
| exception | 异常设定,指定Lambda可能抛出的异常类型(如noexcept表示不抛出任何异常)。 |
| return type | 返回类型;可省略,编译器会通过decltype根据函数体返回值自动推断。 |
| function body | 函数体,Lambda核心逻辑的实现区域。 |
核心语法细节(结合示例)
外部变量捕获
Lambda的核心特性是捕获外部作用域变量,主要分为值捕获、引用捕获、隐式捕获等类型,以下是具体示例:
值捕获(默认,只读)
值捕获会拷贝外部变量到Lambda内部,默认情况下Lambda是const函数,无法修改值捕获的变量。
#include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
int a = 1;
int b = 2;
// [a,b] 以值方式捕获a、b,Lambda内部只读
int sum = [a, b]()->int{return a+b;}();
cout << "sum: " << sum << endl; // 输出:sum: 3
return 0;
}
- 关键说明:
- 末尾的
()是Lambda的调用符号,匿名Lambda可直接调用;也可赋值给具名函数对象后调用:// 将匿名Lambda赋值给函数对象f auto f = [a, b]()->int{return a+b;}; int sum = f(); // 调用,效果与直接调用一致
- 末尾的
引用捕获(可读写)
引用捕获会绑定外部变量的引用,Lambda内部可修改原变量(无需mutable),需注意变量生命周期(避免悬垂引用)。
#include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
int a = 1;
int b = 2;
// [&a, b] 混合捕获:a引用捕获(可修改),b值捕获(只读)
int sum = [&a, b]()->int{a=100; return a+b;}();
cout << "sum: " << sum << endl; // 输出:sum: 102
cout << "a: " << a << endl; // 输出:a: 100(原变量被修改)
return 0;
}
隐式捕获(批量捕获)
当外部变量较多时,可通过通配符批量捕获,分为值捕获([=])和引用捕获([&]):
// 隐式值捕获:捕获所有外部变量(只读)
auto f1 = [=]()->int{return a + b;};
// 隐式引用捕获:捕获所有外部变量(可读写)
auto f2 = [&]()->int{a=200; return a + b;};
混合隐式+显式捕获
支持“隐式+显式”组合,需注意:显式捕获的类型需与隐式捕获不同(避免冲突):
int a = 1, b = 2, c = 3;
// 隐式值捕获所有变量,显式引用捕获c(仅c可修改)
auto f3 = [=, &c](){c=10; return a+b+c;};
空捕获([])
不捕获任何外部变量,仅依赖形参列表接收参数,是最简洁的Lambda形式:
auto add = [](int x, int y){return x + y;};
cout << add(3,4) << endl; // 输出:7
mutable关键字的使用
默认情况下Lambda是const函数,值捕获的变量不可修改;mutable可取消常量性,允许修改值捕获的变量(仅修改内部拷贝,不影响原变量),使用时形参列表不可省略:
#include <iostream>
using namespace std;
int main() {
int a = 10;
// mutable使用:形参列表()不可省略(即使无参数)
auto f = [a]() mutable {a = 20; cout << "Lambda内a: " << a << endl;};
f(); // 输出:Lambda内a: 20
cout << "外部a: " << a << endl; // 输出:外部a: 10(仅修改内部拷贝)
return 0;
}
返回类型的自动推断
Lambda可省略返回类型,编译器通过decltype自动推断,但需注意:函数体有多个return语句时,返回类型需一致,否则需显式声明:
// 省略返回类型:编译器推断为int
auto f1 = [](int x){return x * 2;};
// 错误示例:return类型不一致,需显式声明返回类型
// auto f2 = [](int x){if(x>0) return 1; else return 2.0;};
// 正确示例:显式声明返回类型为double
auto f2 = [](int x)->double{if(x>0) return 1.0; else return 2.0;};
形参列表的典型应用(STL算法库)
Lambda的形参列表核心场景是配合STL算法库使用,算法会自动将容器元素传递给Lambda的形参,示例如下:
#include <array>
#include <algorithm>
#include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
// 定义STL数组容器(一维数组)
array<int, 5> arr = {1,2,3,4,5};
// count_if:统计容器中满足条件的元素数量
// Lambda形参m由count_if自动传递(对应容器的每个int元素)
int evenNum = count_if(arr.begin(), arr.end(), [](int m){return m%2==0;});
cout << "偶数数目是: " << evenNum << endl; // 输出:偶数数目是: 2
return 0;
}
拓展
Lambda的本质:闭包(Closure)
Lambda表达式在编译期会被编译器转换为一个匿名的闭包类,Lambda的调用等价于调用该类的operator()成员函数:
- 值捕获:闭包类的成员变量存储外部变量的拷贝;
- 引用捕获:闭包类的成员变量存储外部变量的引用;
mutable:对应闭包类的operator()成员函数被声明为非const。
Lambda与函数指针/函数对象的对比
| 类型 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| Lambda表达式 | 简洁、支持捕获外部变量、匿名 | 不可复用(除非赋值给函数对象) | 一次性操作、STL算法配合 |
| 函数指针 | 复用性强、兼容C语言 | 不支持捕获外部变量、语法繁琐 | 跨C/C++场景、通用回调 |
| 类函数对象 | 可存储状态、复用性强 | 代码冗余(需定义类) | 需复用且需维护状态的场景 |
Lambda的更多典型应用场景
STL链表排序(补充操作练习示例)
#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
list<int> lst = {5,2,8,1,3};
// Lambda作为sort的比较函数,实现链表升序排序
lst.sort([](int a, int b){return a < b;});
// 遍历输出排序后的链表
for(auto num : lst) {
cout << num << " "; // 输出:1 2 3 5 8
}
return 0;
}
回调函数(异步/事件处理场景)
#include <iostream>
#include <functional>
using namespace std;
// 模拟回调函数触发逻辑
void triggerCallback(function<void(int)> cb, int val) {
cout << "触发回调,传入值:" << val << endl;
cb(val);
}
int main() {
int base = 10;
// Lambda作为回调函数,捕获外部变量base
triggerCallback([base](int val){
cout << "回调处理结果:" << val + base << endl;
}, 5); // 输出:触发回调,传入值:5 → 回调处理结果:15
return 0;
}
统计链表中指定条件的元素数量(补充操作练习)
#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
list<int> lst = {1,2,3,4,5,6,7,8};
// 统计链表中大于4的元素数量
int count = count_if(lst.begin(), lst.end(), [](int num){return num > 4;});
cout << "大于4的元素数量:" << count << endl; // 输出:4
return 0;
}
Lambda使用注意事项
- 引用捕获的生命周期:若Lambda的生命周期超过被捕获变量的生命周期,会导致悬垂引用(如捕获局部变量后,Lambda被传递到函数外部调用)。
- 空捕获的安全性:空捕获
[]的Lambda不依赖外部变量,是最安全的Lambda形式,可放心传递。 - mutable的作用范围:仅修改值捕获的内部拷贝,不会影响外部原变量;引用捕获无需mutable即可修改原变量。
- 与函数指针的转换:无捕获的Lambda可通过
+转换为函数指针,有捕获的Lambda不可转换:// 无捕获的Lambda转换为函数指针 auto f = [](int x){return x*2;}; int (*fp)(int) = +f; cout << fp(3) << endl; // 输出:6

浙公网安备 33010602011771号