C++20 中的 ranges 与 view 的实战应用

在 C++20 中,ranges 和 view 为容器操作提供了更简洁、表达力更强的语法。它们允许我们像处理函数式编程中的序列一样,对 STL 容器进行链式操作,而不必显式编写循环或临时容器。下面以一个常见的“过滤、变换、排序、聚合”流程为例,演示如何使用 ranges 和 view 来完成任务。

1. 基础示例:过滤、变换、聚合

假设我们有一个 `std::vector

`,想要完成以下步骤: 1. 过滤掉所有负数; 2. 将剩余的数乘以 2; 3. 按升序排序; 4. 计算总和。 传统写法: “`cpp std::vector v = {3, -1, 4, 1, 5, 9, -2, 2}; std::vector tmp; for (int x : v) if (x >= 0) tmp.push_back(x * 2); std::sort(tmp.begin(), tmp.end()); int sum = 0; for (int x : tmp) sum += x; “` 使用 ranges 和 view 只需: “`cpp #include #include #include #include int main() { std::vector v = {3, -1, 4, 1, 5, 9, -2, 2}; int sum = std::ranges::fold_left( std::ranges::views::transform( std::ranges::views::filter(v, [](int x){ return x >= 0; }), [](int x){ return x * 2; } ), 0, std::plus() ); std::cout = 0; }) | std::ranges::views::transform([](int x){ return x * 2; }); for (int x : view) { // 这里会一次性过滤并变换,真正的拷贝只在这里发生 } “` ## 3. 自定义 view 如果标准库的 view 不满足需求,可以自定义一个 view。最简单的办法是使用 `std::ranges::view_interface`,并实现 `begin()`/`end()`。 “`cpp #include #include template class square_view : public std::ranges::view_interface> { Iter first_; Iter last_; public: square_view(Iter first, Iter last) : first_(first), last_(last) {} auto begin() const { return std::views::transform(first_, [](auto x){ return x * x; }); } auto end() const { return last_; } }; template auto square(R&& r) { return square_view{std::ranges::begin(r), std::ranges::end(r)}; } “` 使用示例: “`cpp std::vector v = {1, 2, 3, 4}; for (int x : square(v)) { std::cout result(pipeline.begin(), pipeline.end()); “` ## 5. 性能与实践建议 – **避免无意义的复制**:视图本身不复制元素,只有在终端操作(如 `std::ranges::to_vector`)时才会产生新容器; – **使用 `views::common`**:当需要多次遍历时,`views::common` 可将 view 转化为支持随机访问的容器视图,消除迭代器失效风险; – **保持链式简洁**:太长的链式表达式可能导致调试困难,可将中间结果命名或拆分。 通过上述技术,你可以在 C++20 中用更少的代码、更多的表达力来完成复杂的数据处理任务,充分利用 STL 的现代化特性。

发表评论