在 C++20 之前,对容器进行一系列转化、过滤、排序等操作时,往往需要写一堆嵌套的算法调用,或者手动写循环。C++20 引入了 ranges 库,让这些操作变得既简洁又高效。更进一步,ranges 还支持“管道式”语法,使代码像 Unix 管道一样清晰。本文将从概念、语法到实际案例,逐步剖析如何在 C++20 中使用 ranges 与管道式操作。
1. ranges 基础概念
1.1 什么是 range
在 C++20 之前,STL 的算法接受的是 迭代器区间(begin/end)。ranges 通过 range 抽象,把 容器、迭代器区间、生成器 等统一起来。一个 range 对象必须满足 begin() 和 end() 成员函数,返回可比较、可解引用的迭代器。
1.2 views
views 是对原始 range 的一种“懒惰”视图。常见的 views 包括 std::views::filter, std::views::transform, std::views::take, std::views::drop, std::views::reverse, std::views::split, std::views::common 等。每个 view 都是一个高阶函数,返回一个新的 range。
2. 经典范例:筛选偶数并平方
#include <iostream>
#include <vector>
#include <ranges>
#include <algorithm>
int main() {
std::vector <int> data{1,2,3,4,5,6,7,8,9,10};
auto result = data
| std::views::filter([](int x){ return x % 2 == 0; })
| std::views::transform([](int x){ return x * x; });
for (int v : result) {
std::cout << v << ' ';
}
std::cout << '\n';
}
输出: 4 16 36 64 100
说明:filter 先筛选偶数,transform 再做平方。整个过程 懒执行,只有在遍历 result 时才会产生值。
3. 管道式语法(pipe)
C++20 引入了 | 运算符,使 range 的链式调用更自然。std::ranges::pipe 把一个视图函数与前一个 range 连接起来。上述例子正是使用了管道式语法。
小技巧:如果你不想每次都写
std::views::, 可以用namespace rv = std::ranges::views;进行别名。
4. 实际应用:从文件读取行并去重
#include <fstream>
#include <string>
#include <unordered_set>
#include <vector>
#include <ranges>
#include <algorithm>
int main() {
std::ifstream fin("input.txt");
if (!fin) return 1;
std::unordered_set<std::string> seen;
std::vector<std::string> uniq;
auto lines = std::ranges::istream_view<std::string>(fin)
| std::views::filter([&seen](auto const& s){ return seen.insert(s).second; });
std::ranges::copy(lines, std::back_inserter(uniq));
// 打印结果
for (const auto& line : uniq) {
std::cout << line << '\n';
}
}
此代码从 input.txt 读取每一行,利用 unordered_set 进行去重,并在同一行实现 filter。
5. 使用 ranges 与管道式操作的优势
| 传统方式 | C++20 ranges + 管道 |
|---|---|
| 代码冗长,易出错 | 简洁明了,易读易维护 |
| 需要多次遍历 | 懒执行,必要时才遍历 |
| 受限于已有算法 | 通过组合 views 可构建复杂流程 |
| 性能受迭代器解引用成本影响 | 通过 views::common 或 views::all 控制迭代器类型 |
6. 注意事项
- 性能:虽然 ranges 设计为懒惰,但在极端性能敏感场景下,最好先测量与传统方法的差异。
- 迭代器失效:如果你对原始容器做了修改(插入/删除),需要重新创建 views。
- 自定义 view:如果需要更复杂的转换,可以继承
std::ranges::view_interface并实现begin、end。
7. 结语
C++20 的 ranges 与管道式操作为容器算法带来了新的表达方式。它们让代码更接近自然语言表达,减少了样板代码,让程序员把注意力集中在业务逻辑上。熟练掌握后,你会发现写 STL 代码从未如此优雅。祝你在 C++20 的世界里玩得开心!