首页 > Coding > C++20 Ranges

C++20 Ranges

2024年1月28日 发表评论 阅读评论

Ranges 是C++20 提供的一套对范围的统一抽象和操作库。ranges 指可迭代的序列,它可以包括任何能够提供迭代器的数据结构, 如 vector, list, etc. 引入 ranges 可以使迭代的处理更简洁直观灵活。

我们知道 STL algorithms 利用迭代器对数据进行操作。比如我们需要对一个 vector 进行排序, 需要将排序的范围的迭代器做为参数传递给 sort() 方法:

这种写法很灵活。但更多的时候,我们是想对整个 vector 进行排序,传入迭代器反而是多余的操作了。引入 Ranges 即可简化这一操作。

在 ranges 库中,默认是对整个范围进行操作。当然,也可以像原来一样,使用迭代器来指定范围:

这是一种简化操作的方式。但 ranges 更重要的优势在于,它允许你以函数式编程的方式来操作 STL algorithm 。

例如:
在引入 ranges 之前,如果我们需要对一个数组进行多项操作,我们可能需要一些变量来存储中间值。

而引入 ranges 后,可以不再需要中间变量:

ranges 将多个操作使用管道操作符 | 连接在一起,不仅省去了中间变量,还使得代码更简洁易读。

View

view 是一个轻量级的 range。view 的构建、复制与销毁都是在常量时间内完成的,与view 内的元素多寡无关。view 由 range adaptor 创建。 view 中 “显示” 的元素由 range adaptor 决定。在上面的例子中,view 显示的是 从数据 v 中过滤偶数元素,并将这些元素乘以 10 。

注意这里使用了 "显示" 而不是 "拥有" 。这是因为 view 是惰性求值(evaluated lazily)的 。即在上例中,filtertransform 并没有被调用,因为 result 没有被使用。

这段代码中,在 ' - ' 被输出之前,没有任何其它输出,之后 'f_x' 't_x' 才开始被输出。当然,在这里也需要注意管道的运算,和不使用 ranges 不同,管道是按元素进行运算的, 即运算的顺序是:
filter($v_0$)->null, filter($v_1$)->transform($v_1$)->result, filter($v_2$)->transform($v_2$)->result ...
而非
(filter($v_0$), filter($v_1$), $...$) -> (transform($v_1$), ...) -> result.
上例的输出为:

 

Adaptor

adaptor 为 ranges 生成一个随性求值的 view, 如前面所讲,adaptor 生成 view 时, ranges 中的元素不参与运算,只有在访问 view 时,才会对 view 中的元素求值。adaptor 可以串联起来,这是 ranges 的核心之所在,它解决了之前 STL algorithms 不易组合的问题。

使用 adaptor 创建 view 比直接创建 view 更高效,创建 view 的时间复杂度为 $O(1)$ 。

Algorithm

前面用到的 std::rangs::sort(v) 是一个 range algorithm。range algorithm 与 std 命名空间中相应的迭代器的算法几乎完全相同。不同的是它们有 概念强制约束(concept-enforced constraints) , 可以同时接受 range 或 迭代器对来工作。

Function

//TODO

Concept

//TODO

参考

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.