一、Lambda 表达式与函数式编程
Lambda 表达式,说白了就是「匿名函数」。C++11 引入它的时候,我其实挺兴奋的——终于不用为了一个简单的回调,专门写个函数对象了。你想想看,以前写个排序比较器,得先定义一个 struct,重载 operator(),再传进去……现在一行搞定。
但 Lambda 也不是随便用的。捕获机制、泛型、性能开销,这些坑我都踩过。今天咱们就把它聊透。
1.1 Lambda 捕获机制
Lambda 的核心语法长这样:
[捕获列表](参数列表) mutable(可选) -> 返回类型(可选) {
// 函数体
};
捕获列表决定了 Lambda 内部能访问外部哪些变量。我见过不少新手在这里翻车。
| 捕获方式 | 写法 | 说明 |
|---|---|---|
| 值捕获 | [x] | 拷贝外部变量 x,Lambda 内部修改不影响外部 |
| 引用捕获 | [&x] | 引用外部变量 x,内部修改会改变外部 |
| 隐式值捕获 | [=] | 按值捕获所有用到的外部变量 |
| 隐式引用捕获 | [&] | 按引用捕获所有用到的外部变量 |
| 混合捕获 | [=, &x] | 除 x 按引用外,其余按值 |
1.2 mutable 关键字
默认情况下,值捕获的变量在 Lambda 内部是 const 的,不能修改。如果你非要改,加个 mutable:
int count = 0;
auto lambda = [count]() mutable {
count++; // 可以修改,但外部 count 不变
return count;
};
嗯,这里要注意:mutable 只影响值捕获的变量,引用捕获的变量本来就能改,不需要 mutable。
1.3 泛型 Lambda(C++14)
C++14 允许 Lambda 的参数用 auto,这就变成了泛型 Lambda。说白了,就是编译器帮你生成一个模板化的函数调用运算符。
auto add = [](auto a, auto b) {
return a + b;
};
std::cout << add(3, 4) << std::endl; // int
std::cout << add(3.14, 2.86) << std::endl; // double
我个人习惯在写算法回调时用泛型 Lambda,尤其是配合 STL 容器。比如:
std::vector<int> vec = {1, 2, 3, 4, 5};
std::vector<double> dvec = {1.1, 2.2, 3.3};
auto print = [](const auto& v) {
for (const auto& elem : v) {
std::cout << elem << " ";
}
std::cout << std::endl;
};
print(vec); // 打印 int 容器
print(dvec); // 打印 double 容器
你看,一个 Lambda 搞定两种类型,省得写两个函数。
1.4 std::function 与 std::bind
std::function 是个「万能函数包装器」。它可以存任何可调用对象:普通函数、Lambda、函数对象、甚至成员函数指针。
#include <functional>
void hello() {
std::cout << "Hello, World!" << std::endl;
}
int main() {
std::function<void()> func = hello;
func(); // 输出 Hello, World!
func = []() { std::cout << "Lambda says hi!" << std::endl; };
func(); // 输出 Lambda says hi!
}
std::bind 呢?它用来绑定参数,生成一个新的可调用对象。C++11 时代用得挺多,但 C++14 之后,Lambda 基本可以替代它了。
// 用 bind
auto add = [](int a, int b) { return a + b; };
auto add5 = std::bind(add, 5, std::placeholders::_1);
std::cout << add5(3) << std::endl; // 8
// 用 Lambda 更直观
auto add5_lambda = [](int b) { return add(5, b); };
std::cout << add5_lambda(3) << std::endl; // 8
我个人建议:新代码尽量用 Lambda,可读性更好,编译器优化也更充分。
1.5 函数式编程范式
函数式编程的核心思想是:用函数组合解决问题,避免可变状态和副作用。C++ 虽然不是纯函数式语言,但我们可以借鉴它的风格。
几个常见模式:
- 高阶函数:函数作为参数或返回值。Lambda 让这变得极其自然。
- 不可变性:尽量用 const,避免修改外部状态。
- 函数组合:把多个小函数拼成一个大功能。
举个例子,用 STL 算法 + Lambda 实现「过滤出偶数并平方」:
std::vector<int> nums = {1, 2, 3, 4, 5, 6};
std::vector<int> result;
std::copy_if(nums.begin(), nums.end(), std::back_inserter(result),
[](int n) { return n % 2 == 0; });
std::transform(result.begin(), result.end(), result.begin(),
[](int n) { return n * n; });
// result 现在为 {4, 16, 36}
你看,没有循环,没有临时变量,每个步骤都是纯函数。代码意图一目了然。
1.6 知识体系总览
下面这张图帮你理清 Lambda 与函数式编程的核心脉络:
这张图把 Lambda 的四大核心模块串起来了。捕获机制是基础,泛型 Lambda 提升灵活性,std::function 提供存储能力,而函数式编程则是更高层的设计思想。
好了,这一章的内容就到这里。记住:Lambda 不是花架子,它是现代 C++ 里提升代码表达力和性能的重要工具。多写、多用、多思考,你自然能体会到它的妙处。