C++20 Ranges 进阶指南
C++20 Ranges 进阶指南

🔍 C++20 Ranges 进阶指南:Subrange、概念、扩展 Views 与自定义 View 实现


📦 1. std::ranges::subrange:显式表示一个范围

std::ranges::subrange 是一种轻量级结构,用于包装 [begin, end) 的一对迭代器,可以传给 ranges 算法。

✨ 用法

 1#include <ranges>
 2#include <vector>
 3#include <iostream>
 4
 5int main() {
 6    std::vector<int> v{10, 20, 30, 40, 50};
 7
 8    auto first = v.begin() + 1;
 9    auto last = v.begin() + 4;
10
11    std::ranges::subrange r(first, last);
12
13    for (auto x : r)
14        std::cout << x << " ";  // 输出: 20 30 40
15}

✅ 优点

  • 可以显式构造一个不完整容器的范围。

  • 可作为 views 或算法的中间输出传递。

  • 完全兼容 std::ranges 系统。


🧱 2. range Concepts:约束范围类型

C++20 引入了 Concepts,为 ranges 提供了精确的约束判断。

🌐 常用 Concepts

Concept意义
std::ranges::range是一个 range(有 begin/end)
std::ranges::input_range可输入遍历
std::ranges::forward_range可多次遍历
std::ranges::view是惰性视图
std::ranges::sized_range支持 size()
std::ranges::common_rangebeginend 类型相同

✅ 示例:限制函数参数为 forward_range

1template<std::ranges::forward_range R>
2void print_range(const R& r) {
3    for (auto&& x : r) std::cout << x << " ";
4}

🧩 3. 自定义 View 实现(DIY)

如果你需要自定义一个特殊的视图逻辑,比如“滑动窗口”、“滑动最大值”等,可以自定义 view。

🛠 模板

 1template<std::ranges::view V>
 2requires std::ranges::forward_range<V>
 3class my_view : public std::ranges::view_interface<my_view<V>> {
 4    V base_;
 5
 6public:
 7    my_view() = default;
 8    my_view(V base) : base_(std::move(base)) {}
 9
10    auto begin() {
11        return std::ranges::begin(base_);
12    }
13
14    auto end() {
15        return std::ranges::end(base_);
16    }
17};
  • 必须继承 view_interface(提供 empty(), size() 等接口)。

  • 内部需保存 base view 或 range。

  • 必须满足 begin()/end() 的要求。

这是构造自定义视图的基础框架,你可以在 begin()end() 中返回定制迭代器,实现自定义行为。


🧪 4. range-v3 中的扩展 views

标准库中的 views::zipviews::enumerate 等尚未收录,但可用第三方库 range-v3

示例 1:views::zip

 1#include <range/v3/view/zip.hpp>
 2
 3std::vector<int> a{1, 2, 3};
 4std::vector<char> b{'a', 'b', 'c'};
 5
 6auto zipped = ranges::views::zip(a, b);
 7
 8for (auto [x, y] : zipped) {
 9    std::cout << x << " - " << y << "\n";  // 输出: 1 - a, 2 - b, ...
10}

示例 2:views::enumerate

1#include <range/v3/view/enumerate.hpp>
2
3std::vector<std::string> v{"apple", "banana", "pear"};
4
5for (auto [i, s] : ranges::views::enumerate(v)) {
6    std::cout << i << ": " << s << "\n";
7}

如果你在 C++20 标准库中找不到这些 view,可以手动移植或使用 range-v3。


⚠️ 5. 注意事项与性能陷阱

  • 惰性遍历,非缓存views 默认每次访问都重新计算,适合一次遍历,不适合频繁随机访问。

  • 嵌套视图需注意生命周期:不要返回本地 view,确保引用合法。

  • 组合嵌套可能产生难以调试的类型:可以使用 decltype(auto) 保留类型。

1auto r = vec | std::views::filter(...) | std::views::transform(...);
2// 不建议写:std::vector<int> r = ... // 错误
  • 转容器需手动 copy:views 是懒惰的,转 vector 需显式调用 to_vector() 或构造。

🧠 总结升级版

模块内容
ranges::subrange显式构造任意范围
Concepts限制参数/模板范围类型
自定义 view构造新视图逻辑(如滑窗、分组等)
range-v3 扩展zip, enumerate, join 等非标视图
注意事项views 非缓存、组合类型复杂、手动容器转换

最后修改于 2025-07-08 18:57