输入关键词开始搜索

C++20 Ranges — 管道式数据处理

从迭代器到 Range

// C++17 — 迭代器对,啰嗦
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8};
std::vector<int> even;
std::copy_if(v.begin(), v.end(),
             std::back_inserter(even),
             [](int x) { return x % 2 == 0; });
std::transform(even.begin(), even.end(), even.begin(),
               [](int x) { return x * x; });

// C++20 — 管道风格
auto result = v
    | std::views::filter([](int x) { return x % 2 == 0; })
    | std::views::transform([](int x) { return x * x; })
    | std::ranges::to<std::vector<int>>();
// {4, 16, 36, 64} — 懒求值,只在 to<> 时执行

核心 View 速查

View效果示例
filter保留满足条件的v | filter([](int x){return x>0;})
transform映射转换v | transform([](int x){return x*2;})
take / drop取前N / 跳过前Nv | take(3)
take_while / drop_while条件取/跳v | take_while([](int x){return x<10;})
reverse逆序v | views::reverse
enumerate带索引v | enumerate
join展平嵌套vv | join
split按分隔符拆分字符串str | split('\n')
iota无限序列views::iota(0)
common统一 iterator/sentinel 类型v | common

懒求值

// Ranges 是懒求值:管道定义操作,不立即执行
auto pipeline = views::iota(0)              // 0, 1, 2, ...
              | views::filter(isEven)       // 过滤偶数
              | views::transform(square)    // 平方
              | views::take(5);             // 取 5 个

// 只有遍历时才真正计算
for (int x : pipeline)  // 0, 4, 16, 36, 64
    std::cout << x << ' ';

Projection — 投影

struct Person { std::string name; int age; };
std::vector<Person> people = {{"Bob", 30}, {"Alice", 25}};

// C++17: 需要 lambda
std::sort(people.begin(), people.end(),
    [](auto &a, auto &b) { return a.name < b.name; });

// C++20: projection 直接指定比较成员
std::ranges::sort(people, {}, &Person::name);  // 按 name 排序
std::ranges::sort(people, {}, &Person::age);   // 按 age 排序

// 第二个参数 {} = 默认比较器(less)
// 第三个参数 = projection:提取要比较的字段

算法升级

// C++20 std::ranges 版算法——直接用容器,不再需要 begin/end
std::ranges::sort(v);
std::ranges::find(v, 42);
std::ranges::copy_if(v, std::back_inserter(out), pred);

// 返回值更丰富
auto result = std::ranges::minmax(v);
// result.min, result.max — 结构化返回

自定义 Range

// 实现一个只读的循环缓冲区 View
template <typename T>
class RingView : public std::ranges::view_interface<RingView<T>> {
    const T *data_;
    size_t size_, head_;
public:
    RingView(const T *d, size_t n, size_t h)
        : data_(d), size_(n), head_(h) {}

    auto begin() const { return data_ + head_; }
    auto end() const {
        return std::default_sentinel;  // C++20 sentinel
    }
    // 需要定义迭代器类型时...
};

// 更简单:用已有的 range adaptor 组合
auto top5 = v | views::reverse | views::take(5);

常见陷阱

// 陷阱 1: View 是借用语义 — 原数据不能提前析构
auto bad() {
    std::vector<int> v = {1, 2, 3};
    return v | views::reverse;  // ❌ v 析构 → view 悬空
}

// 陷阱 2: filter 后的元素不再是随机访问
auto evens = v | views::filter(isEven);
// evens[0]  // ❌ filter 后的 range 不是 random_access

// 陷阱 3: view 本身不拥有数据
auto v = views::iota(0);  // 这是 OK 的 — iota 自己生成数据
auto w = someVec | views::drop(5);  // w 只引用 someVec,不拷贝