C++20 中的范围(Ranges)如何使用?

在 C++20 中,标准库新增了一个强大的特性:范围(Ranges)库。它把 STL 容器、迭代器、算法等概念整合到了一套统一的接口中,使代码更简洁、表达更自然。下面通过几个实例来演示如何使用 Range,掌握其核心概念和常见工具。


1. 范围(Ranges)基础概念

  • View(视图):对原始序列的“轻量级”视图,支持链式操作,避免了中间临时容器。典型例子:std::views::filterstd::views::transformstd::views::reverse 等。
  • View 适配器:将原始序列转换为视图的函数,例如 std::views::all 把任何可迭代对象转换为可迭代视图。
  • Pipe 操作符(|):实现链式调用。range | view1 | view2 | view3 依次把视图应用到 range 上。

2. 简单使用示例

2.1 过滤偶数

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

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

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

    for (int n : evens)
        std::cout << n << ' ';   // 输出:2 4 6 8 10
}

2.2 变换为平方

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

for (int n : squares)
    std::cout << n << ' ';   // 输出:1 4 9 16 25 ...

2.3 组合使用

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

for (int n : even_squares)
    std::cout << n << ' ';   // 输出:4 16 36 64 100

3. 逆序视图与反转

auto rev = nums | std::views::reverse;

for (int n : rev)
    std::cout << n << ' ';   // 输出:10 9 8 7 6 5 4 3 2 1

注意:reverse 并不创建新容器,而是仅通过逆向迭代器遍历。


4. std::ranges::for_eachstd::ranges::copy

4.1 for_each

std::ranges::for_each(even_squares, [](int n){ std::cout << n << '\n'; });

4.2 copy 到输出流

std::ranges::copy(even_squares, std::ostream_iterator <int>{std::cout, " "});
// 输出:4 16 36 64 100

5. 自定义视图适配器

假设我们想实现一个 take 视图,截取前 N 个元素。

template<std::ranges::input_range R>
auto take_view(R&& r, std::size_t n)
{
    struct view {
        R range;
        std::size_t count;
        auto begin() { return std::ranges::begin(range); }
        auto end() {
            auto it = std::ranges::begin(range);
            std::advance(it, std::min(count, std::ranges::size(range)));
            return it;
        }
    };
    return view{ std::forward <R>(r), n };
}

int main() {
    std::vector <int> data{1,2,3,4,5};
    for (int x : take_view(data, 3))
        std::cout << x << ' ';   // 输出:1 2 3
}

提示:在 C++20 里实现完整的自定义视图需要遵循 std::ranges::view_interface 并实现 begin()end()size() 等成员;上例简化实现,适用于教学演示。


6. 常见视图适配器汇总

视图名称 作用
std::views::all 将任何可迭代对象转为视图
std::views::filter 过滤元素
std::views::transform 映射元素
std::views::reverse 逆序
std::views::drop 跳过前 N 个元素
std::views::take 取前 N 个元素
std::views::join 连接嵌套容器
std::views::stride 取每 K 个元素
std::views::common 让视图支持 size()empty()

7. 性能与注意事项

  • 延迟求值:视图是惰性求值的,只有在遍历时才计算,避免不必要的拷贝。
  • 生命周期:视图内部存储对原始容器的引用,使用时请确保原始容器的生命周期至少与视图相同。
  • 兼容性:Range 库在 C++20 中已正式标准化,支持主流编译器(gcc≥10, clang≥11, MSVC≥19.30)。

8. 小结

C++20 的 Range 库让算法与容器的组合变得更像函数式编程。通过 views::filterviews::transformviews::reverse 等视图适配器,代码更短、易读、可组合。掌握它们后,你可以在日常项目中写出更清晰、更高效的迭代逻辑。

练习建议:在项目中替换一段旧式 for 循环,使用 Range 重写。观察性能与可维护性的差异,进一步体会 Range 的强大。

发表评论