C++20 中的 Ranges 与 Views 如何简化容器遍历?

在 C++20 里,std::ranges 提供了一套统一、强类型的容器视图与算法,彻底改变了我们对容器遍历和组合的思维方式。下面通过几个典型示例,演示如何使用 Ranges 与 Views 来让代码既简洁又安全。

1. 基本概念

  • Range:任何满足 begin()end()(或 cbegin()cend())且返回迭代器的对象。标准库中的 std::vectorstd::arraystd::string 都是 Range。
  • View:对 Range 的“延迟评估”包装,产生一个新的、可被算法使用的 Range,但不会复制数据。典型的 View 如 std::views::filterstd::views::transformstd::views::take 等。
  • Algorithmstd::ranges::for_eachstd::ranges::sort 等,接受 Range 作为参数。

2. 过滤与转换

假设我们有一个整数数组,想要找到所有偶数并打印它们的平方。

#include <iostream>
#include <vector>
#include <ranges>

int main() {
    std::vector <int> nums{1, 2, 3, 4, 5, 6};

    auto evens = nums | std::views::filter([](int n){ return n % 2 == 0; });
    auto squares = evens | std::views::transform([](int n){ return n * n; });

    std::ranges::for_each(squares, [](int n){ std::cout << n << ' '; });
    // 输出: 4 16 36
}
  • std::views::filter 产生一个新的 View,只保留满足条件的元素。
  • std::views::transform 将每个元素按给定函数变换。

3. 组合视图

Views 可以无限链式组合:

auto result = nums
              | std::views::filter([](int n){ return n > 2; })
              | std::views::transform([](int n){ return n * 3; })
              | std::views::take(3);

上述代码先筛选出大于 2 的数字,再乘以 3,最后只取前 3 个结果。整个过程无须显式循环,且仅在需要时进行迭代。

4. 与标准算法配合

Ranges API 让传统算法与视图天然兼容:

std::vector <int> data{9, 1, 4, 2, 7};
auto sorted = data | std::views::sort;   // std::views::sort 不是标准,示意
// 实际使用时可直接:
std::ranges::sort(data);

std::ranges::sort 等算法接受 Range,而不是迭代器对。若需在不修改原容器的前提下进行排序,可以先生成一个 std::views::all 的 copy,然后在 copy 上排序。

5. 自定义视图

有时需要特定的视图,例如按步长取值。可以通过继承 std::ranges::view_base 并实现 begin()end() 来自定义:

template <typename R, std::ptrdiff_t Step = 1>
class step_view : public std::ranges::view_base {
    R rng_;
public:
    explicit step_view(R rng) : rng_(std::move(rng)) {}

    auto begin() {
        return std::ranges::begin(rng_);
    }

    auto end() {
        // 这里简单示例,实际需要实现步长逻辑
        return std::ranges::end(rng_);
    }
};

随后可以通过 rng | step_view<...> 语法使用。

6. 性能与安全性

  • 延迟评估:Views 在使用前不会立即遍历容器,只有在真正迭代时才产生结果,避免不必要的计算。
  • 类型安全:编译器在编译时检查迭代器类型与算法兼容性,降低运行时错误。
  • 可组合性:链式调用让复杂操作拆分为可读性高的表达式。

7. 小结

C++20 的 Ranges 与 Views 为容器操作带来了新的语义与范式。相比传统的 for‑loop 或手写迭代器,它们:

  • 代码更简洁、可读性更强;
  • 更易于组合与复用;
  • 减少副作用和错误。

在日常编码中,建议先尝试将常见操作(过滤、映射、排序、分块)改写为 View 链式调用,逐步体会其带来的好处。随着 C++23 进一步完善 Ranges,未来的 STL 将会更加“函数式”,欢迎你也来一起探索吧!

发表评论