深入理解 C++ Lambda 表达式与函数式编程
一、引言:从函数指针到 Lambda
在 C++ 98/03 时代,我们主要依靠函数指针和仿函数(Function Object)来传递行为:
代码语言:javascript
AI代码解释
cpp复制编辑boolisEven(int x) {
return x % 2 == 0;
}
随着 C++11 的到来,引入了强大的 Lambda 表达式,让我们可以像 Python、JavaScript 一样优雅地定义 匿名函数、闭包函数和高阶函数。
Lambda 表达式使得 C++ 函数式编程成为可能,成为现代 C++ 的重要特性。
二、Lambda 表达式语法详解
1. 语法格式
代码语言:javascript
AI代码解释
cpp复制编辑[capture](parameters) -> return_type {
function_body;
}
2. 简单示例
代码语言:javascript
AI代码解释
cpp复制编辑auto add = [](int a, int b) -> int {
return a + b;
};
std::cout << add(3, 4); // 输出 7
3. 省略返回类型(由 auto 推导)
代码语言:javascript
AI代码解释
cpp复制编辑auto multiply = [](int a, int b) {
return a * b;
};
三、捕获列表(Capture List)
捕获外部变量的方式:
|
捕获方式 |
含义 |
|---|---|
|
[=] |
值捕获所有外部变量 |
|
[&] |
引用捕获所有外部变量 |
|
[x] |
值捕获变量 x |
|
[&x] |
引用捕获变量 x |
|
[=, &y] |
默认值捕获,但 y 使用引用 |
|
[this] |
捕获当前对象指针(用于类中) |
示例:
代码语言:javascript
AI代码解释
cpp复制编辑int x = 10;
auto f = [x]() { return x + 1; }; // 捕获副本auto g = [&x]() { x += 1; }; // 修改 x
四、闭包对象与类型推导
Lambda 表达式其实是一个匿名类的 闭包对象(Closure Object),它重载了 operator(),因此可像函数一样调用。
代码语言:javascript
AI代码解释
cpp复制编辑auto lambda = [](int x) { return x * x; };
等价于:
代码语言:javascript
AI代码解释
cpp复制编辑struct__lambda {
intoperator()(int x) const {
return x * x;
}
};
C++20 引入 decltype(auto) 和 concept,更好支持 Lambda 类型。
五、Lambda 与 STL 算法结合
Lambda 与 STL 算法完美契合,提升代码表达力。
1. std::for_each
代码语言:javascript
AI代码解释
cpp复制编辑std::vector<int> v = {1, 2, 3};
std::for_each(v.begin(), v.end(), [](int x) {
std::cout << x << " ";
});
2. std::sort
代码语言:javascript
AI代码解释
cpp复制编辑std::vector<int> v = {3, 1, 4};
std::sort(v.begin(), v.end(), [](int a, int b) {
return a > b;
});
3. std::count_if
代码语言:javascript
AI代码解释
cpp复制编辑int count = std::count_if(v.begin(), v.end(), [](int x) {
return x % 2 == 0;
});
六、可变 Lambda(mutable)
默认情况下,捕获变量为只读,若想修改值副本,需加 mutable。
代码语言:javascript
AI代码解释
cpp复制编辑int x = 10;
auto f = [x]() mutable {
x += 1; // 修改副本,不影响外部 xreturn x;
};
f(); // 返回 11
std::cout << x; // 仍为 10
七、Lambda 表达式作为返回值
Lambda 不能直接推导类型,需要使用 std::function 或 auto。
代码语言:javascript
AI代码解释
cpp复制编辑#include<functional>std::function<int(int)> makeAdder(int base) {
return [base](int x) {
return base + x;
};
}
auto add5 = makeAdder(5);
std::cout << add5(3); // 输出 8
八、高阶函数:函数作为参数或返回值
示例:执行器模式
代码语言:javascript
AI代码解释
cpp复制编辑voidexecute(const std::function<void()> &func) {
func();
}
execute([] {
std::cout << "Hello from Lambda!" << std::endl;
});
九、Lambda 与类成员函数
在类中使用 Lambda:
代码语言:javascript
AI代码解释
cpp复制编辑classPrinter {
public:
voidrun() {
int count = 3;
auto printer = [this, count]() {
for (int i = 0; i < count; ++i)
std::cout << "Value: " << i + value << std::endl;
};
printer();
}
private:
int value = 10;
};
捕获 [this] 让 Lambda 可访问成员变量。
十、Lambda 实现递归(Y Combinator 技巧)
Lambda 本身无法直接递归调用自己,可以通过 std::function 或 Y Combinator 技巧实现。
代码语言:javascript
AI代码解释
cpp复制编辑std::function<int(int)> fib = [&](int n) {
return (n <= 1) ? n : fib(n - 1) + fib(n - 2);
};
或者更现代写法(C++14):
代码语言:javascript
AI代码解释
cpp复制编辑auto fib = [](auto self, int n) -> int {
return (n <= 1) ? n : self(self, n - 1) + self(self, n - 2);
};
std::cout << fib(fib, 6); // 输出 8
十一、Lambda 与并发:线程任务函数
代码语言:javascript
AI代码解释
cpp复制编辑#include<thread>intmain() {
int x = 42;
std::thread t([x]() {
std::cout << "Thread value: " << x << std::endl;
});
t.join();
}
在多线程任务中,Lambda 极其便捷地绑定任务上下文。
十二、Lambda 性能与底层实现
1. Lambda 实际为类对象(闭包),编译期生成匿名类。
2. 若无捕获,Lambda 转换为函数指针,零开销。
代码语言:javascript
AI代码解释
cpp复制编辑auto f = [] (int x) { return x * x; }; // 无捕获int (*fp)(int) = f; // OK
3. 有捕获时为对象,不能转函数指针。
十三、C++20 捕获 by move(移动捕获)
代码语言:javascript
AI代码解释
cpp复制编辑std::unique_ptr<int> up = std::make_unique<int>(10);
auto lambda = [ptr = std::move(up)]() {
std::cout << *ptr << std::endl;
};
此技术称为 初始化捕获,C++14 起支持,C++20 支持 =std::move(...)。
十四、图解:Lambda 捕获与类型推导
代码语言:javascript
AI代码解释
text复制编辑int a = 10, b = 20;
auto f = [a, &b]() mutable {
a++; // 修改副本,不影响外部
b++; // 修改引用,影响外部
};
图示:
代码语言:javascript
AI代码解释
markdown复制编辑Lambda 对象:
--------------------------| a(副本) = 10 || b(引用) → 外部 b |
--------------------------
十五、实战案例:高阶筛选器生成器
代码语言:javascript
AI代码解释
cpp复制编辑auto make_filter = [](int threshold) {
return [threshold](int x) {
return x > threshold;
};
};
auto filter10 = make_filter(10);
std::cout << filter10(15); // true
std::cout << filter10(5); // false
该 Lambda 工厂函数生成高阶条件过滤器。
十六、总结与建议
|
特性 |
支持版本 |
描述 |
|---|---|---|
|
Lambda 表达式 |
C++11 |
匿名函数支持 |
|
mutable Lambda |
C++11 |
修改值捕获副本 |
|
初始化捕获 |
C++14 |
[val = expr] |
|
泛型 Lambda |
C++14 |
[ ](auto x){} |
|
捕获 by move |
C++20 |
[ptr = std::move(...) ] |
✅ 学习建议:
- 多使用 Lambda 与 STL 算法结合提升代码简洁性
- 熟悉闭包、引用捕获、值捕获的语义差异
- 掌握递归 Lambda、线程任务、函数对象替代等实战技巧
- 学会 std::function 包装函数对象并作为参数传递
更多推荐
所有评论(0)