Lambda 与算法组合
定义与作用
Lambda 表达式是内联匿名函数对象,与 STL 算法组合使用是实现"声明式编程"的核心方式——用简短的代码描述"做什么",而非用循环描述"怎么做"。
// 传统方式:手写循环
std::vector<int> result;
for (int x : v)
if (x > 10) result.push_back(x * 2);
// Lambda + 算法:声明式
std::vector<int> result;
std::copy_if(v.begin(), v.end(), std::back_inserter(result),
[](int x) { return x > 10; });
std::transform(result.begin(), result.end(), result.begin(),
[](int x) { return x * 2; });
核心原理
Lambda 编译期展开
捕获语义
完整示例
示例一:飞翔科技多维数据筛选
场景说明:高英用 Lambda + 算法组合对飞翔科技用户数据进行多条件筛选。
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
struct User {
std::string name;
int age;
double spend; // 消费金额
bool isVip;
std::string city;
};
void printUsers(const std::string& title, const std::vector<User>& users) {
std::cout << title << " (" << users.size() << " 人):\n";
for (const auto& u : users)
std::cout << " " << u.name << " | " << u.age << "岁 | ¥"
<< u.spend << " | " << (u.isVip ? "VIP" : "普通")
<< " | " << u.city << "\n";
}
int main() {
std::vector<User> users = {
{"大翔", 28, 188888.8, true, "广州"},
{"白歌", 32, 166666.6, true, "广州"},
{"小崔", 25, 88888.8, false, "深圳"},
{"黄俪", 26, 66666.6, false, "广州"},
{"李眉", 35, 123456.7, true, "广州"},
{"孔蓝", 27, 232000.0, true, "广州"},
{"赵鸣", 24, 88888.8, false, "北京"},
{"高英", 30, 166666.6, true, "上海"},
{"杨英", 23, 52000.0, false, "广州"},
{"孙鹤", 22, 32000.0, false, "深圳"},
};
// 1. copy_if:筛选 VIP 且花费大于 10 万的用户
std::vector<User> highValueVIPs;
std::copy_if(users.begin(), users.end(),
std::back_inserter(highValueVIPs),
[](const User& u) { return u.isVip && u.spend > 100000; });
printUsers("=== 高价值 VIP ===", highValueVIPs);
// 2. 链式组合:先筛广州用户、再计算总消费
double gzTotal = 0;
std::for_each(users.begin(), users.end(),
[&gzTotal](const User& u) {
if (u.city == "广州") gzTotal += u.spend;
});
std::cout << "\n广州地区总消费: ¥" << gzTotal << "\n";
// 3. 值捕获 + mutable:按年龄分组统计
int threshold = 28;
auto youngUsers = std::count_if(users.begin(), users.end(),
[threshold](const User& u) { return u.age < threshold; });
std::cout << "年龄 < " << threshold << " 的用户: " << youngUsers << " 人\n";
// 4. 引用捕获:找出非 VIP 且低消费的需关注用户
std::vector<std::string> attentionList;
double spendLimit = 60000;
std::for_each(users.begin(), users.end(),
[&attentionList, spendLimit](const User& u) {
if (!u.isVip && u.spend < spendLimit)
attentionList.push_back(u.name + "(" + u.city + ")");
});
std::cout << "\n需关注用户 (非VIP + 消费<¥" << spendLimit << "):\n";
for (const auto& name : attentionList)
std::cout << " " << name << "\n";
// 5. 泛型 Lambda (C++14):通用分组统计
auto groupBy = [](auto& container, auto getKey) {
std::unordered_map<
std::decay_t<decltype(getKey(*container.begin()))>,
int
> groups;
for (const auto& item : container)
++groups[getKey(item)];
return groups;
};
auto cityGroups = groupBy(users, [](const User& u) { return u.city; });
std::cout << "\n地区分布:\n";
for (const auto& [city, count] : cityGroups)
std::cout << " " << city << ": " << count << " 人\n";
}
预期输出:
=== 高价值 VIP === (4 人):
大翔 | 28岁 | ¥188889 | VIP | 广州
白歌 | 32岁 | ¥166667 | VIP | 广州
李眉 | 35岁 | ¥123457 | VIP | 广州
孔蓝 | 27岁 | ¥232000 | VIP | 广州
广州地区总消费: ¥677000
年龄 < 28 的用户: 5 人
需关注用户 (非VIP + 消费<¥60000):
杨英(广州)
孙鹤(深圳)
地区分布:
广州: 6 人
深圳: 2 人
北京: 1 人
上海: 1 人
逐段分析:
copy_if+ Lambda 替代手写if + push_back循环for_each+ 引用捕获&gzTotal实现累积统计threshold值捕获使 Lambda 携带外部参数count_if一步完成计数- C++14 泛型 Lambda(
auto参数)实现通用groupBy函数
示例二:Lambda 初始化捕获——资源管理
场景说明:小崔用 C++14 初始化捕获将 unique_ptr 转移到 Lambda 中。
#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>
#include <functional>
struct DatabaseConn {
std::string dbName;
explicit DatabaseConn(std::string name) : dbName(std::move(name)) {
std::cout << " 连接数据库: " << dbName << "\n";
}
~DatabaseConn() { std::cout << " 断开: " << dbName << "\n"; }
std::string query(const std::string& sql) {
return "[" + dbName + "] 结果: " + sql + " -> OK";
}
};
int main() {
std::cout << "=== 飞翔科技数据库查询 ===\n\n";
// 初始化捕获:将 unique_ptr 移动到 Lambda 中
auto conn = std::make_unique<DatabaseConn>("飞翔科技-主库");
auto executor = [conn = std::move(conn)](const std::string& sql) {
return conn->query(sql);
};
// conn 已移动,不能再使用
// 执行多次查询
std::vector<std::string> queries = {
"SELECT * FROM users WHERE vip=1",
"SELECT SUM(spend) FROM orders",
"SELECT COUNT(*) FROM logins WHERE date='2026-06-14'",
};
std::cout << "执行查询:\n";
for (const auto& q : queries)
std::cout << " " << executor(q) << "\n";
// Lambda 可以传入 std::function
std::function<std::string(const std::string&)> fn = std::move(executor);
std::cout << "\n通过 std::function 调用:\n";
std::cout << " " << fn("SELECT version()") << "\n";
}
预期输出:
=== 飞翔科技数据库查询 ===
连接数据库: 飞翔科技-主库
执行查询:
[飞翔科技-主库] 结果: SELECT * FROM users WHERE vip=1 -> OK
[飞翔科技-主库] 结果: SELECT SUM(spend) FROM orders -> OK
[飞翔科技-主库] 结果: SELECT COUNT(*) FROM logins WHERE date='2026-06-14' -> OK
通过 std::function 调用:
[飞翔科技-主库] 结果: SELECT version() -> OK
断开: 飞翔科技-主库
逐段分析:
[conn = std::move(conn)]:C++14 初始化捕获,将unique_ptr转移到闭包- 移动后 Lambda 独占连接的所有权,析构时自动释放
std::function可包装此 Lambda,实现在异步回调中延迟执行- RAII:数据库连接随 Lambda 生命周期管理,无需手动关闭
易错场景与面试考点
易错场景
1. 引用捕获 + 悬垂引用
auto makeLambda() {
int x = 42;
return [&x]() { return x; }; // ❌ x 在函数返回后销毁
}
2. for_each 中修改容器结构
std::vector<int> v{1, 2, 3};
// std::for_each(v.begin(), v.end(), [&](int x) {
// if (x == 2) v.push_back(4); // ❌ 迭代器可能失效
// });
3. 捕获 this 后对象被销毁
struct Widget {
void schedule() {
// ❌ 如果 this 在回调执行前销毁 → 悬垂
asyncCall([this]() { this->doWork(); });
}
};
面试考点
| 考点 | 要点 |
|---|---|
| 值捕获 vs 引用捕获 | 值捕获安全但不可变,引用捕获注意生命周期 |
| mutable 关键字 | 允许修改值捕获的副本 |
| 泛型 Lambda | C++14 auto 参数,等价于模板化的 operator() |
| 初始化捕获 | C++14 [x = expr],支持 move-only 类型 |
| 与 std::function 配合 | Lambda 可转为 std::function,有虚函数调用开销 |
| 闭包类型 | 每个 Lambda 表达式有唯一匿名类型,即使签名相同 |
| 捕获时机 | 值捕获在 Lambda 定义时发生,非调用时 |