C++20 引入了 Ranges 库,为我们提供了更强大、更直观的方式来处理容器、迭代器和算法之间的组合。相比传统的 STL,Ranges 通过视图(view)和适配器(adapter)实现了惰性求值、链式组合和更高的可读性。本文将深入探讨 Ranges 的核心概念,并通过实战案例演示如何在日常开发中高效利用这些功能。
1. Ranges 的核心概念
- Range:表示一段可遍历的元素序列。任何满足
begin/end的对象都是 Range。 - View:对 Range 的一个无状态的、惰性求值的变换,类似于延迟计算。视图是链式可组合的。
- Adapter:对视图进行包装,提供更丰富的功能。常见的适配器包括
filter,transform,take,drop,reverse等。 - View adaptor 与 Algorithm:通过
std::ranges::命名空间下的函数实现算法的链式调用。
2. 经典案例:筛选、映射、排序
假设我们有一个 `std::vector
`,需要完成以下任务: 1. 过滤出偶数 2. 对每个偶数乘以 2 3. 按降序排序 4. 取前 5 个 使用传统 STL 代码通常是: “`cpp std::vector data = {1,2,3,4,5,6,7,8,9,10}; std::vector result; std::copy_if(data.begin(), data.end(), std::back_inserter(result), [](int x){ return x % 2 == 0; }); std::transform(result.begin(), result.end(), result.begin(), [](int x){ return x * 2; }); std::sort(result.begin(), result.end(), std::greater ()); if (result.size() > 5) result.resize(5); “` 使用 Ranges,代码可精简为: “`cpp #include #include #include #include int main() { std::vector 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 * 2; }) | std::views::reverse // 先逆序,后排序得到降序 | std::views::take(5); for (int x : result) std::cout << x << ' '; } “` 输出: “` 20 16 12 8 4 “` **说明**: – `filter` 与 `transform` 直接作用于 `data`,不需要中间容器。 – `reverse` + `take` 实现“降序取前5”的逻辑。若要真正排序,可使用 `std::ranges::sort`,但其需要 Materialize 视图为容器,或自行实现 `std::ranges::partial_sort`。 #### 3. 结合算法:`partition`, `unique`, `binary_search` **分区**: “`cpp auto&& [evens, odds] = std::ranges::partition(data, [](int x){ return x % 2 == 0; }); “` **去重**: “`cpp std::ranges::unique(data); // 对已排序的 Range 去重 “` **二分搜索**: “`cpp bool found = std::ranges::binary_search(data, 7); // data 必须已排序 “` #### 4. 视图的惰性求值与性能 视图是惰性求值的,意味着在遍历之前不会进行任何计算。例如,链式调用 `filter | transform | reverse` 并不会立即生成任何中间容器,只有在真正遍历(如 `for (auto x : view)`)时才会逐个元素计算。这在处理大规模数据时可以显著降低内存占用。 #### 5. Ranges 与传统 STL 的互操作 – `std::ranges::to()`:将视图 materialize 为 `std::vector`。如果你需要将 Ranges 结果传递给仅接受容器的 API,这非常方便。 – `std::views::common`:把不确定是否为通用 Range 的视图转为通用视图,保证 `begin()` 和 `end()` 返回同类型。 – `std::ranges::subrange`:对迭代器范围创建一个子范围。 #### 6. 常见错误与调试技巧 – **忘记包含 ` `**:C++20 视图在 “ 头文件中声明,确保开启 C++20 标准编译。 – **视图返回的不是容器**:若需要容器,请使用 `std::ranges::to()` 或 `std::ranges::to()`。 – **排序视图**:若想对视图中的元素排序,需要先 materialize 为容器或使用 `std::ranges::sort` 对可写视图。 #### 7. 小结 C++20 的 Ranges 与算法适配器提供了一种更加函数式、声明式的方式来处理容器。通过组合视图、使用算法适配器,我们可以在保持代码可读性的同时,减少中间数据结构,提升性能。建议从日常数据过滤与映射开始尝试,将 Ranges 逐步迁移到已有项目中,体验其带来的代码整洁与效率提升。 祝你编码愉快,探索更多 C++20 的精彩功能!