Lambda表达式是C++11引入的核心特性之一,本质是一种匿名函数,可以捕获作用域内的变量,无需单独定义函数名,就能实现简洁、灵活的代码编写,尤其适合作为回调函数、算法参数(如STL算法)等场景,大幅提升代码可读性和开发效率。
一、Lambda表达式的核心作用
Lambda的核心价值的是"轻量、匿名、可捕获",解决了传统函数的两个痛点:
-
无需单独定义函数(尤其是只使用一次的简单逻辑),减少代码冗余;
-
可以直接捕获当前作用域的变量(局部变量、类成员等),无需通过参数传递,简化逻辑。
最常见的使用场景:配合STL算法(如sort、for_each)、多线程回调(如thread、async)、条件变量的wait条件判断等。
二、Lambda表达式的语法结构(必背)
Lambda的语法格式固定,核心由5部分组成,其中只有"函数体"是必须的,其余部分可根据需求省略:
[捕获列表] (参数列表) mutable noexcept -> 返回值类型 { 函数体; }
1. 各部分详解(从左到右)
(1)捕获列表 [ ]:最关键部分,控制Lambda能否访问外部变量
捕获列表用于指定"Lambda能使用哪些外部变量",有5种常见写法,必须牢记:
| 捕获方式 | 语法 | 说明 |
|---|---|---|
| 空捕获 | [] | 不捕获任何外部变量,Lambda内部只能使用自身参数和全局变量 |
| 值捕获 | [var1, var2] | 捕获指定变量(var1、var2),拷贝一份到Lambda内部,默认不可修改(除非加mutable) |
| 引用捕获 | [&var1, &var2] | 捕获指定变量的引用,Lambda内部操作会直接修改外部变量(注意变量生命周期,避免悬空引用) |
| 值捕获所有变量 | [=] | 捕获当前作用域所有外部变量,均为值拷贝,默认不可修改 |
| 引用捕获所有变量 | [&] | 捕获当前作用域所有外部变量,均为引用,可修改外部变量 |
注意:捕获列表不能混合冲突,比如[=, &var]是允许的(所有变量值捕获,唯独var引用捕获),但[&, var]也是允许的(所有变量引用捕获,唯独var值捕获);但[=, &]、[var, =]是错误的。
(2)参数列表 ( ):和普通函数的参数列表一致
用于接收外部传递给Lambda的参数,语法和普通函数完全相同,可省略(当无参数时)。
cpp
// 无参数,可省略()
auto func1 = [] { cout << "无参数Lambda" << endl; };
// 有参数,和普通函数一致
auto func2 = [](int a, int b) { return a + b; };
(3)mutable:可选,解除值捕获的"只读限制"
值捕获的变量,默认在Lambda内部是"只读"的,无法修改;加上mutable后,允许修改Lambda内部的拷贝(不会影响外部原变量)。
cpp
int x = 10;
// 错误:值捕获x,默认不可修改
// auto func = [x] { x++; };
// 正确:加mutable,可修改内部拷贝
auto func = [x]() mutable { x++; cout << "内部x:" << x << endl; };
func(); // 输出:内部x:11
cout << "外部x:" << x << endl; // 输出:外部x:10(原变量未变)
(4)noexcept:可选,声明Lambda不会抛出异常
和普通函数的noexcept作用一致,用于告诉编译器,Lambda内部不会抛出异常,可提升性能,尤其在多线程、STL算法中常用。
cpp
auto func = [](int a) noexcept { return a * 2; };
(5)返回值类型 -> 类型:可选,编译器可自动推导
如果Lambda函数体只有一条return语句,编译器会自动推导返回值类型,可省略"-> 返回值类型";如果函数体有多条语句且有返回值,必须显式指定返回值类型。
cpp
// 自动推导返回值(int),可省略-> int
auto add = [](int a, int b) { return a + b; };
// 必须显式指定返回值,否则编译错误
auto func = [](int a) -> double {
if (a > 0) return 1.0;
else return 0.5;
};
(6)函数体 { }:Lambda的核心逻辑
和普通函数的函数体一致,可编写任意合法的C++代码,可使用捕获的变量、参数列表中的参数。
三、Lambda表达式的基本使用示例
示例1:基础用法(无捕获、有参数、自动推导返回值)
cpp
#include <iostream>
using namespace std;
int main() {
// 定义Lambda,计算两个数的和
auto add = [](int a, int b) {
return a + b;
};
// 调用Lambda,和调用普通函数一样
int result = add(3, 5);
cout << "3 + 5 = " << result << endl; // 输出:3 + 5 = 8
return 0;
}
示例2:捕获外部变量(值捕获+引用捕获)
cpp
#include <iostream>
using namespace std;
int main() {
int x = 10, y = 20;
// 值捕获x,引用捕获y
auto func = [x, &y]() {
// x是值拷贝,不可修改(无mutable);y是引用,可修改
// x++; // 错误:值捕获默认只读
y++;
cout << "内部x:" << x << ", 内部y:" << y << endl;
};
func(); // 输出:内部x:10, 内部y:21
cout << "外部x:" << x << ", 外部y:" << y << endl; // 输出:外部x:10, 外部y:21
return 0;
}
示例3:配合STL算法(最常用场景)
Lambda最常用的场景就是作为STL算法的参数,替代繁琐的函数对象或全局函数。
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> vec = {3, 1, 4, 1, 5, 9};
// 1. 用Lambda排序(降序)
sort(vec.begin(), vec.end(), [](int a, int b) {
return a > b; // 降序排序规则
});
// 2. 用Lambda遍历输出
for_each(vec.begin(), vec.end(), [](int val) {
cout << val << " ";
}); // 输出:9 5 4 3 1 1
return 0;
}
示例4:配合多线程(回调函数)
cpp
#include <iostream>
#include <thread>
using namespace std;
int main() {
int num = 0;
// 引用捕获num,在子线程中修改
thread t([&num]() {
for (int i = 0; i < 10000; i++) {
num++;
}
});
t.join(); // 等待子线程结束
cout << "num = " << num << endl; // 输出:num = 10000
return 0;
}
四、Lambda表达式的注意事项(面试高频)
1. 捕获变量的生命周期问题(重点坑)
引用捕获的变量,Lambda内部持有变量的引用,如果外部变量生命周期结束(比如出作用域销毁),Lambda再访问该引用会导致悬空引用,程序崩溃。
cpp
// 错误示例:引用捕获局部变量,变量销毁后Lambda访问悬空引用
auto getFunc() {
int x = 10;
return [&x]() { cout << x << endl; }; // x是局部变量,出函数销毁
}
int main() {
auto func = getFunc();
func(); // 崩溃:访问悬空引用
return 0;
}
解决方案:如果Lambda要在外部使用,优先使用值捕获(拷贝一份,不受外部变量生命周期影响)。
2. Lambda的本质是"函数对象"
Lambda在编译时会被编译器自动转换为一个"匿名的函数对象"(也叫仿函数),因此Lambda可以赋值给std::function(C++11的函数包装器),方便存储和传递。
cpp
#include <functional>
#include <iostream>
using namespace std;
int main() {
// Lambda赋值给function
function<int(int, int)> add = [](int a, int b) { return a + b; };
cout << add(2, 3) << endl; // 输出:5
return 0;
}
3. 捕获列表不能捕获全局变量、静态变量
全局变量、静态变量属于全局作用域,Lambda内部可以直接访问,无需在捕获列表中声明(捕获列表只用于捕获"局部变量")。
cpp
#include <iostream>
using namespace std;
int g_val = 100; // 全局变量
int main() {
static int s_val = 200; // 静态变量
// 无需捕获,直接访问全局变量、静态变量
auto func = []() {
cout << "全局变量:" << g_val << endl;
cout << "静态变量:" << s_val << endl;
};
func(); // 正常输出:全局变量:100 静态变量:200
return 0;
}
4. mutable的作用范围
mutable只允许修改"值捕获"的拷贝,不会影响外部原变量;引用捕获的变量,即使不加mutable,也可以修改(因为引用本身就是直接操作原变量)。
5. Lambda不能递归调用(除非借助std::function)
Lambda是匿名函数,自身无法直接调用自己;如果需要递归,必须先将Lambda赋值给std::function,再在函数体中调用该function。
cpp
#include <functional>
#include <iostream>
using namespace std;
int main() {
// 先声明function,再赋值Lambda
function<int(int)> factorial;
factorial = [&factorial](int n) {
return n == 1 ? 1 : n * factorial(n - 1);
};
cout << factorial(5) << endl; // 输出:120(5的阶乘)
return 0;
}
五、Lambda表达式的核心总结(笔记直接抄)
-
Lambda是C++11匿名函数,核心语法:
[捕获列表](参数列表) mutable noexcept -> 返回值类型 {函数体;}; -
捕获列表是核心,分5种:[]、[var]、[&var]、[=]、[&],不可混合冲突;
-
值捕获默认只读,需修改内部拷贝加mutable;引用捕获需注意变量生命周期;
-
返回值可自动推导(单条return),多条return需显式指定;
-
最常用场景:STL算法参数、多线程回调、简单逻辑的临时函数;
-
本质是函数对象,可赋值给std::function,支持递归(需借助function)。
Lambda的核心优势是"简洁、灵活",能大幅简化代码,尤其在需要临时函数的场景中,替代传统函数或函数对象,提升代码可读性和开发效率,是C++多线程、STL开发的必备技能。