在 C++20 中,标准库新增了视图(views)和管道(pipelines),为处理容器和流式数据提供了更简洁、更表达式化的方式。本文以一个常见的场景——“从学生成绩列表中找出平均分高于某阈值的学生姓名,并按成绩降序输出”——为例,展示如何利用视图与管道完成任务,并深入剖析其实现细节、性能优势以及常见陷阱。
1. 先行准备:数据模型
#include <iostream>
#include <vector>
#include <string>
#include <ranges>
#include <algorithm>
#include <numeric>
struct Student {
std::string name;
int score;
};
using namespace std::ranges::views;
假设我们有一个 `std::vector
`,填充了若干学生数据: “`cpp std::vector students = { {“Alice”, 88}, {“Bob”, 76}, {“Charlie”, 92}, {“Diana”, 67}, {“Ethan”, 85}, {“Fiona”, 91} }; “` ## 2. 传统做法(for 循环 + 传统 STL) “`cpp int main() { double avg = std::accumulate(students.begin(), students.end(), 0.0, [](double sum, const Student& s){ return sum + s.score; }) / students.size(); std::vector filtered; std::copy_if(students.begin(), students.end(), std::back_inserter(filtered), [avg](const Student& s){ return s.score > avg; }); std::sort(filtered.begin(), filtered.end(), [](const Student& a, const Student& b){ return a.score > b.score; }); for (const auto& s : filtered) std::cout avg; }) // 过滤 | transform([](const Student& s){ return std::pair{s.name, s.score}; }) // 转成 pair | sort([](auto a, auto b){ return a.second > b.second; }) // 排序 | elements ; // 取出姓名 // 3. 输出 for (const auto& name : result) std::cout ` | | `sort` | 对视图进行排序 | 默认按元素可比较性;我们自定义比较器 | | `elements ` | 从 `pair` 中取出第一个元素 | 取姓名 | 通过视图,**所有步骤都只遍历一次**,并且在编译时已知所有转换,避免了不必要的中间容器。 ## 4. 性能剖析 – **延迟执行**:视图是惰性求值的,直到真正需要数据时才触发。`filter`、`transform`、`sort` 之间不会产生临时容器。 – **内存占用**:只保留一个原始容器 `students`,视图本身不占用额外内存。 – **缓存友好**:对 `students` 的访问是连续的,符合 CPU 缓存特性。 – **排序复杂度**:`sort` 的复杂度仍为 `O(n log n)`,但因为视图只访问一次,所以相对于传统做法的多次遍历具有明显优势。 ## 5. 常见陷阱与技巧 1. **避免多次遍历** 视图默认惰性,但如果你在中间插入了 `to_vector()` 或 `to_list()` 等终止操作,可能会导致不必要的拷贝。只在需要时才调用终止操作。 2. **自定义视图** 如果标准视图不足以表达需求,可以编写自定义 `std::ranges::view_base`,实现 `begin()`、`end()`、`iterator_concept` 等。 3. **递归视图** 通过 `std::ranges::view::join` 可以展平嵌套容器,例如 `vector>`。 4. **错误的 `sort` 用法** `sort` 视图会在内部构造临时容器来保存排序结果;如果想避免临时容器,可在 `transform` 前先调用 `to_vector()`,再对 `vector` 排序。 5. **C++23 进一步提升** C++23 将 `ranges::views::filter` 直接提供 `const auto&` 版本,避免无谓的复制。 ## 6. 结语 C++20 的视图与管道为容器操作提供了类似函数式编程的表达方式,既提升了代码可读性,又保留了极高的性能。掌握它们,你可以轻松写出既简洁又高效的代码。下次在需要对大量数据进行过滤、变换、排序等操作时,试着使用视图链式编程,让代码更优雅、更现代。祝你编码愉快!