在C++20中,std::ranges提供了一套强大的函数式编程工具,允许你在容器上以声明式方式进行过滤、映射、排序等操作。下面我们将演示如何结合std::ranges::view::filter和std::ranges::view::transform,实现链式的过滤与转换,并在实际代码中展示其用法与性能优势。
1. 需求场景
假设我们有一份学生成绩列表,包含姓名、学号和分数。现在需要完成以下任务:
- 过滤出分数高于80分的学生。
- 将剩下的学生按分数降序排序。
- 将最终结果转换为字符串格式,便于打印。
传统的实现往往需要多次遍历或显式使用循环。使用std::ranges,可以一次性描述整个流程,代码更简洁且易于维护。
2. 关键概念回顾
| 名称 | 作用 | 示例 |
|---|---|---|
std::ranges::view::filter |
根据谓词过滤元素 | auto filtered = xs | std::views::filter([](auto&& x){return x>10;}); |
std::ranges::view::transform |
对元素做映射 | auto mapped = xs | std::views::transform([](auto&& x){return x*2;}); |
std::ranges::view::take |
取前N个元素 | auto taken = xs | std::views::take(5); |
std::ranges::view::reverse |
反转视图 | auto rev = xs | std::views::reverse; |
std::ranges::sort |
对容器排序 | std::ranges::sort(container, cmp); |
注意:
std::views是std::ranges::view的简化别名,方便使用。
3. 示例代码
#include <iostream>
#include <vector>
#include <string>
#include <ranges>
#include <algorithm>
// 学生结构体
struct Student {
std::string name;
std::string id;
int score;
};
int main() {
// 初始化数据
std::vector <Student> students = {
{"Alice", "S001", 95},
{"Bob", "S002", 78},
{"Cathy", "S003", 88},
{"David", "S004", 62},
{"Eve", "S005", 92},
{"Frank", "S006", 85}
};
// 1. 过滤:score > 80
// 2. 排序:score 降序
// 3. 转换:格式化为字符串
// 先过滤,再排序
std::vector <Student> filtered;
filtered.reserve(students.size());
std::ranges::copy(
students | std::views::filter([](const Student& s){ return s.score > 80; }),
std::back_inserter(filtered)
);
// 排序
std::ranges::sort(filtered, std::less{}, &Student::score, std::greater <int>());
// 映射为字符串
auto result = filtered | std::views::transform([](const Student& s){
return s.name + " (" + s.id + "): " + std::to_string(s.score);
});
// 打印结果
std::cout << "Top students (score > 80):\n";
for (const auto& str : result) {
std::cout << " - " << str << '\n';
}
return 0;
}
代码说明
- 过滤:
std::views::filter([](const Student& s){ return s.score > 80; })会产生一个视图,仅包含满足条件的学生。该视图是惰性求值,真正遍历发生在std::ranges::copy时。 - 排序:对过滤后的临时容器
filtered进行排序。C++20中std::ranges::sort可以直接使用比较器与成员指针来指定排序规则。 - 转换:
std::views::transform([](const Student& s){...})将Student对象映射为字符串。此时视图仍然是惰性的,最终打印时才真正执行。 - 打印:简单循环输出字符串即可。
4. 性能与优势
- 惰性求值:
std::views使用迭代器惰性实现,只在真正需要时才访问元素,避免了多余拷贝。 - 链式语义:使用
|操作符可以像Unix管道一样直观地描述数据流,代码可读性大幅提升。 - 类型安全:所有操作都在编译期完成类型检查,避免了运行时错误。
- 兼容性:
std::ranges在C++20之后的标准中被正式采纳,所有主流编译器均支持。
5. 进一步扩展
- 多条件过滤:可组合多个
filter,如| std::views::filter(cond1) | std::views::filter(cond2)。 - 分页:使用
std::views::take与std::views::drop实现。 - 并行:在支持并行算法的实现中,结合
std::execution::par可进一步加速。
6. 小结
通过std::ranges的视图与算法组合,C++20提供了一种优雅且高效的方式来处理容器数据。它不但让代码更简洁,而且在保持性能的同时提高了可维护性。希望本文能帮助你在项目中快速上手并利用std::ranges实现更多功能。