以前使用range-v3, C++21中增加了range的支持, 但是感觉差异还是蛮大的. 慢慢收集总结一下.

使用std::rangerange-v3的差别

使用range-v3,最简单的方式是#include <range/v3/all.hpp>,把所有的头文件都放进去就是了。编译速度慢就慢了,大不了扔到预编译头文件里面去。
使用std::range,好像标准库没有给出一个合集,所以需要自己一点点加进去。目前,rangesalgorithm是至少需要的。

range-v3和std::range的对比-views

大家都有的:

  • all
  • cartesian_product
  • chunk, chunk_by
  • concat
  • counted
  • common
  • drop, drop_while
  • empty
  • enumerate
  • filter
  • iota,range-v3的ints也比较接近
  • keys, values
  • join
  • repeat
  • reverse
  • single
  • split
  • stride
  • take, take_while
  • transform
  • zip
  • to

—std::range有的

  • adjacent, slide:注意两者的区别。功能是一样的,但是返回值类型和参数不一样。跟range-v3的sliding在功能上近似。
  • lazy_split
  • elements:这个很牛逼,在range-v3里面没有找到类似的东西。
  • zip_transform
  • adjacent_transform
  • join_with

—range-v3有的

  • zip_with
  • take_exactly, take_last,
  • split_when
  • drop_last, drop_exactly
  • addressof
  • adjacent_filter, adjacent_remove_if
  • any_view<T>(rng)
  • c_str
  • cachel
  • const_
  • cycle
  • delimit
  • for_each
  • generate, generate_n
  • indirect
  • intersperse
  • ints
  • linear_distribute
  • move
  • partial_sum
  • remove, remove_if
  • repeat_n
  • replace, replace_if
  • sample
  • sliding
  • tail
  • tokenize
  • trim
  • unbounded
  • unique

adaptor类和adaptor方法

std::ranges的手册中,Range adaptors,Range factories都是有两个的。比如,ranges::reverse_viewviews::reverse。他们放在一起。

但是两个不是一个东西。ranges::reverse_view是一个类,views::reverse是一个方法。功能上一样,使用上有区别:

1
2
3
4
5
std::vector<int> r = {1, 2, 3, 4, 5};
// 适配器类
std::ranges::reverse_view rv(r);
// 适配器方法
auto reversed = r | std::views::reverse;

建议使用views::reverse这种。

algorithnm和action的概念

range-v3中,有viewsactions的概念。views是创建惰性视图,知道需要时才进行计算,而action直接处理存在的集合,并立即执行所需的操作。所以,range-v3里面的action列表,很多项在views里面都有对应的同名项。

std::ranges::sort的projection

std::ranges::sort的接口形式和传统的std::sort有一点不同,它除了表示range的参数外,还有一个comp参数和一个proj参数。其中,comp是比较函数,而proj是projection函数。proj会对range的每个成员计算,将结果给comp函数来做比较。

这个参数比较有意思,我们知道,传统的std::sort也有comp参数,它完成了这里的投影和计算两个功能,而在ranges库里面,把它分开了。为啥呢?或许是为了解耦比较运算?

comp的默认值是{},此时使用ranges::less,如果要修改,我们需要人工提供, 例如,ranges::greater()。从cppreference里面的说法,std::ranges::less必须实现所有六个比较运算符:<, <=, >, >=, == and != .

proj的默认值是std::identity,就是不改变的意思。

比如下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
std::vector vec{ -1, 2, 5, 3, -6, 7, -12 };
auto print = [](int i) { std::cout << i << " "; };
// 对原始值做排序(按照从小到大)
std::cout << "sort by value: ";
std::ranges::sort(vec);
std::ranges::for_each(vec, print);
// 对绝对值做排序(按照从小到大)
std::cout << "\nsort by abs: ";
std::ranges::sort(vec, {}, [](int i) {return std::abs(i); });
std::ranges::for_each(vec, print);
// 使用std::ranges::greater()作为comp参数比较原始值,按照从大到小
std::cout << "\nsort by value with greater:";
std::ranges::sort(vec, std::ranges::greater());
std::ranges::for_each(vec, print);

这里我的困惑主要是在于,为什么要把projection和compare两个分开。背后的思想究竟是什么,是不是我猜测的解耦,还是有别的意图?