跳转到内容

C++20 范围

来自代码酷

C++20范围(Ranges)[编辑 | 编辑源代码]

介绍[编辑 | 编辑源代码]

C++20范围(Ranges)是C++标准库的一个重要扩展,旨在提供更简洁、更高效的方式来处理序列数据(如容器、数组或生成器)。它引入了新的抽象层,允许开发者以声明式风格编写代码,减少样板代码并提高可读性。范围库的核心思想是将算法和视图(views)与数据源解耦,支持惰性求值和管道操作。

范围库的主要组件包括:

  • 范围概念(Range Concepts):定义序列的抽象要求(如可迭代、可随机访问等)。
  • 视图(Views):惰性计算的序列转换(如过滤、映射、切片等)。
  • 范围算法(Range Algorithms):与标准库算法类似,但直接操作范围。

核心概念[编辑 | 编辑源代码]

范围(Range)[编辑 | 编辑源代码]

一个范围是任何可以被迭代的序列,例如标准库容器(`std::vector`、`std::list`)、原生数组或生成器。在C++20中,范围通过概念定义:

template<typename T>
concept range = requires(T& t) {
    std::ranges::begin(t); // 必须支持begin()
    std::ranges::end(t);   // 必须支持end()
};

视图(View)[编辑 | 编辑源代码]

视图是惰性计算的轻量级范围,不拥有数据。常见的视图操作包括:

  • `std::views::filter`:过滤元素。
  • `std::views::transform`:映射元素。
  • `std::views::take`:取前N个元素。

管道操作符(`|`)[编辑 | 编辑源代码]

管道操作符允许将多个视图操作串联起来,形成数据处理管道:

auto result = data | std::views::filter(pred) | std::views::transform(fn);

代码示例[编辑 | 编辑源代码]

示例1:基本范围操作[编辑 | 编辑源代码]

以下示例展示如何用范围库过滤偶数并平方:

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

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

    // 过滤偶数 -> 平方
    auto result = numbers 
        | std::views::filter([](int n) { return n % 2 == 0; })
        | std::views::transform([](int n) { return n * n; });

    for (int n : result) {
        std::cout << n << " "; // 输出:4 16 36
    }
}

示例2:组合视图[编辑 | 编辑源代码]

将多个视图组合为复杂管道:

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

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

    // 取前5个元素 -> 过滤奇数 -> 平方
    auto pipeline = data 
        | std::views::take(5)
        | std::views::filter([](int x) { return x % 2 != 0; })
        | std::views::transform([](int x) { return x * x; });

    for (int x : pipeline) {
        std::cout << x << " "; // 输出:1 9 25
    }
}

实际应用场景[编辑 | 编辑源代码]

场景1:日志分析[编辑 | 编辑源代码]

假设需要从日志文件中提取错误代码并统计频率:

std::vector<std::string> logs = {"ERROR:404", "INFO:Start", "ERROR:500", "WARN:Timeout"};

auto error_codes = logs 
    | std::views::filter([](const std::string& s) { return s.starts_with("ERROR"); })
    | std::views::transform([](const std::string& s) { return s.substr(6); });

// error_codes包含{"404", "500"}

场景2:游戏开发[编辑 | 编辑源代码]

在游戏对象列表中筛选可见对象并计算距离:

struct GameObject { float x, y; bool is_visible; };

std::vector<GameObject> objects = /* ... */;
auto visible_distances = objects 
    | std::views::filter(&GameObject::is_visible)
    | std::views::transform([](const GameObject& obj) { 
        return std::sqrt(obj.x * obj.x + obj.y * obj.y); 
    });

性能与设计优势[编辑 | 编辑源代码]

  • 惰性求值:视图仅在迭代时计算,节省内存和计算资源。
  • 可组合性:管道操作符允许链式调用,代码更模块化。
  • 类型安全:通过概念约束,编译时检查范围操作的有效性。

范围库的数学基础[编辑 | 编辑源代码]

范围操作可以类比数学中的集合运算: filter(S,p)={xSp(x)} transform(S,f)={f(x)xS}

图表说明[编辑 | 编辑源代码]

以下Mermaid图展示范围管道的处理流程:

graph LR A[原始数据] --> B[filter操作] B --> C[transform操作] C --> D[结果]

总结[编辑 | 编辑源代码]

C++20范围库通过声明式编程风格显著提升了序列操作的表达力。初学者可以从简单的过滤和映射开始,逐步掌握视图组合和自定义范围适配器。高级用户可利用其惰性求值特性优化性能密集型应用。