第8.2章: range 类别
概述
不同的 range 支持的操作并不相同。有些只能被读取一次,有些允许多次遍历,有些允许向后移动,还有一些允许常数时间跳跃或直接访问内存地址。
理解这些差异的一个清晰方式,是把 range 类别看作一条能力层级。每一级都包含上一级的全部能力,并在此基础上增加新的能力。
本章将介绍:
- range 类别的完整层级
- 从
input_range到contiguous_range的可读链条 - 独立的可写分支
output_range - 如何用
if constexpr探测类别
1. 完整的 range 层级
所有 range 类别都是对基础概念 range 的细化。由此,层级分成一条可读链条和一个独立的可写分支:
range
│
├── input_range
│ │
│ ├── forward_range
│ │
│ ├── bidirectional_range
│ │
│ ├── random_access_range
│ │
│ └── contiguous_range
│
└── output_range (separate writable branch)
本章关注可读层级(图中左侧分支)。
2. input_range
最小的可读类别。input_range 支持:
- 只能读取元素一次
- 只能向前移动
- 单遍遍历
3. forward_range
forward_range 具有 input_range 的全部能力,并额外支持:
- 对同一批元素进行多次遍历
- 按相同顺序重新访问元素
forward_range 同时也是 input_range。
4. bidirectional_range
bidirectional_range 具有 forward_range 的全部能力,并额外支持:
- 既能向前也能向后移动
bidirectional_range 同时也是 forward_range。
5. random_access_range
random_access_range 具有 bidirectional_range 的全部能力,并额外支持:
- 常数时间跳到任意位置
- 用
r[i]做下标访问
random_access_range 同时也是 bidirectional_range。
6. contiguous_range
contiguous_range 具有 random_access_range 的全部能力,并额外保证它的元素在程序的抽象地址空间中占据相邻的地址。
当 range 非空时,对任何指向真实元素(而不是 end())的迭代器 it,下面的等式成立:
::std::to_address(it) + 1 == ::std::to_address(it + 1)
这一表述用迭代器刻画连续性,而且不需要解引用。
contiguous_range 同时也是 random_access_range。
7. output_range(独立分支)
output_range 是 input_range 的可写对应物。它允许写入元素,但不要求从 range 读取。
它不属于可读层级,而是在 range 之下自成一支。
有些 range 只可读,有些只可写,还有一些两者都支持。
8. 用 if constexpr 探测 range 类别
range 类别是编译期属性,可以用 if constexpr 和标准概念来探测。
void analyze_vector(::fast_io::vector& v)
{
if constexpr(::std::ranges::contiguous_range<decltype(v)>)
print("vector is a contiguous range\n");
if constexpr(::std::ranges::random_access_range<decltype(v)>)
print("vector is a random_access range\n");
if constexpr(::std::ranges::bidirectional_range<decltype(v)>)
print("vector is a bidirectional range\n");
if constexpr(::std::ranges::forward_range<decltype(v)>)
print("vector is a forward range\n");
if constexpr(::std::ranges::input_range<decltype(v)>)
print("vector is an input range\n");
if constexpr(::std::ranges::output_range<decltype(v), int>)
print("vector is an output range for int\n");
}
由于 contiguous_range 满足所有更弱的可读类别,因此会打印出多行。
9. 目前已介绍的 range 所属的类别
目前介绍过的所有 range 都属于连续 range:
::fast_io::vector::fast_io::string::fast_io::array::fast_io::span::fast_io::string_view::fast_io::cstring_view::fast_io::index_span- C 风格数组
后续章节会介绍不连续的 range。
要点
range是根概念。- 可读层级从
input_range一直上升到contiguous_range。 output_range是一个独立的可写分支。- 每个更强的可读类别都包含所有更弱的类别。
- 连续 range 保证在抽象地址空间中地址相邻。
- 目前介绍过的所有 range 都是连续的。