【C++】零基础入门 · 第 15 节:Lambda 表达式
·
在 C++11 之前,如果你想传递一个「小功能」给函数,通常需要定义一个独立的函数或者函数对象。这种方式不仅繁琐,而且代码分散,可读性差。
Lambda 表达式(也叫匿名函数)的出现,让你可以在需要的地方直接定义「小函数」,代码更简洁、更易读。
1. 什么是 Lambda 表达式?
Lambda 表达式是一种没有名字的函数,你可以把它理解为「随用随定义的临时函数」。
1.1 基本语法
[捕获列表](参数列表) -> 返回类型 {
函数体
}
各个部分的含义:
- 捕获列表:指定 Lambda 可以访问外部作用域的哪些变量
- 参数列表:和普通函数的参数一样
- 返回类型:可以省略,编译器会自动推导
- 函数体:函数的具体逻辑
1.2 最简单的 Lambda
#include <iostream>
using namespace std;
int main() {
// 定义一个最简单的 Lambda
auto sayHello = []() {
cout << "Hello, Lambda!" << endl;
};
// 调用 Lambda
sayHello(); // 输出:Hello, Lambda!
return 0;
}
这里 [] 是捕获列表(空表示不捕获任何外部变量),() 是参数列表(空表示无参数)。
2. 带参数的 Lambda
2.1 基本参数
#include <iostream>
using namespace std;
int main() {
// 带参数的 Lambda
auto add = [](int a, int b) {
return a + b;
};
cout << add(3, 5) << endl; // 输出:8
cout << add(10, 20) << endl; // 输出:30
return 0;
}
2.2 指定返回类型
#include <iostream>
using namespace std;
int main() {
// 显式指定返回类型
auto divide = [](double a, double b) -> double {
if (b == 0) {
return 0;
}
return a / b;
};
cout << divide(10, 3) << endl; // 输出:3.33333
cout << divide(10, 0) << endl; // 输出:0
return 0;
}
大多数情况下,返回类型可以省略,编译器会自动推导。但在以下情况需要显式指定:
- 有多个 return 语句且返回类型不同
- 需要返回引用
- 返回类型无法自动推导
3. 捕获外部变量
Lambda 最强大的特性是可以捕获(访问)外部作用域的变量。
3.1 值捕获 [=]
#include <iostream>
using namespace std;
int main() {
int x = 10;
int y = 20;
// 值捕获:复制外部变量的值
auto lambda = [=]() {
cout << "x = " << x << ", y = " << y << endl;
// x = 30; // 错误!值捕获的变量是只读的
};
x = 30; // 修改外部变量
y = 40;
lambda(); // 输出:x = 10, y = 20(捕获的是定义时的值)
return 0;
}
注意:值捕获会复制变量的值,Lambda 内部看到的是「快照」,外部变量的后续修改不影响 Lambda。
3.2 引用捕获 [&]
#include <iostream>
using namespace std;
int main() {
int x = 10;
int y = 20;
// 引用捕获:捕获变量的引用
auto lambda = [&]() {
cout << "x = " << x << ", y = " << y << endl;
x = 100; // 可以修改外部变量
};
lambda(); // 输出:x = 10, y = 20
cout << "修改后 x = " << x << endl; // 输出:修改后 x = 100
return 0;
}
引用捕获让 Lambda 可以读写外部变量,但也意味着你需要小心悬垂引用问题。
3.3 混合捕获
#include <iostream>
using namespace std;
int main() {
int a = 1;
int b = 2;
int c = 3;
int d = 4;
// 混合捕获:默认值捕获,但 b 和 d 使用引用捕获
auto lambda = [=, &b, &d]() {
// a, c 是值捕获(只读)
// b, d 是引用捕获(可读写)
b = a + c;
d = a * c;
};
lambda();
cout << "b = " << b << endl; // 输出:b = 4
cout << "d = " << d << endl; // 输出:d = 3
return 0;
}
捕获列表的组合方式:
| 捕获列表 | 含义 |
|---|---|
[] |
不捕获任何变量 |
[=] |
所有变量以值方式捕获 |
[&] |
所有变量以引用方式捕获 |
[x] |
只捕获 x(值方式) |
[&x] |
只捕获 x(引用方式) |
[=, &x] |
默认值捕获,x 引用捕获 |
[&, x] |
默认引用捕获,x 值捕获 |
[this] |
捕获当前对象的 this 指针 |
3.4 初始化捕获(C++14)
C++14 引入了初始化捕获,让你可以在捕获列表中创建新变量:
#include <iostream>
#include <string>
using namespace std;
int main() {
// 初始化捕获:在捕获列表中创建新变量
auto lambda = [name = string("Alice"), age = 25]() {
cout << name << " is " << age << " years old" << endl;
};
lambda(); // 输出:Alice is 25 years old
// 移动捕获(C++14)
auto ptr = make_unique<int>(42);
auto lambda2 = [p = move(ptr)]() {
cout << *p << endl;
};
lambda2(); // 输出:42
// cout << *ptr << endl; // 错误!ptr 已经被移动
return 0;
}
4. Lambda 的实际应用
4.1 作为排序依据
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> nums = {5, 2, 8, 1, 9, 3};
// 使用 Lambda 自定义排序
sort(nums.begin(), nums.end(), [](int a, int b) {
return a > b; // 降序排序
});
cout << "降序排序结果:";
for (int num : nums) {
cout << num << " ";
}
cout << endl; // 输出:9 8 5 3 2 1
// 按绝对值排序
vector<int> nums2 = {-5, 2, -8, 1, 9, -3};
sort(nums2.begin(), nums2.end(), [](int a, int b) {
return abs(a) < abs(b);
});
cout << "按绝对值排序:";
for (int num : nums2) {
cout << num << " ";
}
cout << endl; // 输出:1 2 -3 -5 -8 9
return 0;
}
4.2 作为算法的参数
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 使用 Lambda 过滤偶数
auto it = find_if(nums.begin(), nums.end(), [](int n) {
return n % 2 == 0;
});
if (it != nums.end()) {
cout << "第一个偶数是:" << *it << endl; // 输出:2
}
// 使用 Lambda 计算满足条件的元素个数
int evenCount = count_if(nums.begin(), nums.end(), [](int n) {
return n % 2 == 0;
});
cout << "偶数个数:" << evenCount << endl; // 输出:5
// 使用 Lambda 转换元素
vector<int> squares(nums.size());
transform(nums.begin(), nums.end(), squares.begin(), [](int n) {
return n * n;
});
cout << "平方值:";
for (int sq : squares) {
cout << sq << " ";
}
cout << endl; // 输出:1 4 9 16 25 36 49 64 81 100
return 0;
}
4.3 作为回调函数
#include <iostream>
#include <functional>
using namespace std;
// 接受回调函数的高阶函数
void executeWithRetry(int maxRetries, function<bool()> operation) {
for (int i = 0; i < maxRetries; i++) {
cout << "尝试第 " << (i + 1) << " 次..." << endl;
if (operation()) {
cout << "操作成功!" << endl;
return;
}
}
cout << "达到最大重试次数,操作失败" << endl;
}
int main() {
int attempt = 0;
// 使用 Lambda 作为回调
executeWithRetry(3, [&attempt]() -> bool {
attempt++;
// 模拟第 3 次尝试成功
return attempt >= 3;
});
return 0;
}
5. 泛型 Lambda(C++14)
C++14 允许 Lambda 使用 auto 作为参数类型,实现泛型功能:
#include <iostream>
using namespace std;
int main() {
// 泛型 Lambda
auto add = [](auto a, auto b) {
return a + b;
};
cout << add(1, 2) << endl; // 输出:3(int + int)
cout << add(1.5, 2.5) << endl; // 输出:4(double + double)
cout << add(1, 2.5) << endl; // 输出:3.5(int + double)
// 泛型 Lambda 用于打印任意容器
auto printContainer = [](const auto& container) {
for (const auto& item : container) {
cout << item << " ";
}
cout << endl;
};
vector<int> nums = {1, 2, 3, 4, 5};
vector<string> words = {"Hello", "World", "C++"};
printContainer(nums); // 输出:1 2 3 4 5
printContainer(words); // 输出:Hello World C++
return 0;
}
6. constexpr Lambda(C++17)
C++17 允许 Lambda 在编译期求值:
#include <iostream>
using namespace std;
int main() {
// constexpr Lambda
constexpr auto square = [](int x) {
return x * x;
};
// 编译期求值
constexpr int result = square(5);
cout << "5 的平方是:" << result << endl; // 输出:25
// 可以用在需要常量表达式的场景
int arr[square(3)]; // 数组大小为 9
return 0;
}
7. 常见陷阱
7.1 悬垂引用
#include <iostream>
#include <functional>
using namespace std;
function<int()> createLambda() {
int x = 42;
return [&x]() { // 危险!x 是局部变量
return x; // 函数返回后 x 已经销毁
};
}
int main() {
auto lambda = createLambda();
cout << lambda() << endl; // 未定义行为!
// 正确做法:使用值捕获
auto createLambda2 = []() {
int x = 42;
return [x]() { // 值捕获,复制一份
return x;
};
};
auto lambda2 = createLambda2();
cout << lambda2() << endl; // 输出:42
return 0;
}
7.2 捕获 this 指针
#include <iostream>
using namespace std;
class Calculator {
private:
int base;
public:
Calculator(int b) : base(b) {}
auto getAdder() {
// 捕获 this 指针
return [this](int x) {
return base + x;
};
}
};
int main() {
Calculator calc(10);
auto adder = calc.getAdder();
cout << adder(5) << endl; // 输出:15
return 0;
}
8. 总结
Lambda 表达式是现代 C++ 中最常用的特性之一:
- 简洁:不需要单独定义函数,代码更紧凑
- 灵活:可以捕获外部变量,适应各种场景
- 强大:配合 STL 算法,写出更函数式的代码
最佳实践:
- 优先使用
[=]或[&]的简洁形式 - 只在需要时捕获特定变量
- 避免捕获局部变量的引用
- Lambda 体较长时考虑使用普通函数
下一节我们将学习多线程编程,Lambda 在其中会发挥重要作用。
更多推荐



所有评论(0)