了解 zip in range-v3 的工作原理

understanding how zip in range-v3 works

我想了解 ranges::views::zip 如何在 range-v3 中工作。我知道这是一个范围,它允许通过在不同范围内创建元素的元组来在一个循环中迭代多个范围。

std::vector<int> v1 = {0, 1, 2};
std::vector<char> v2 = {'a', 'b', 'c'};


auto zip = ranges::views::zip(v1,v2);
// zip(v1,v2) = [(0,a), (1,b), (2,c)]

ranges::actions::sort(zip);
std::sort(std::begin(zip), std::end(zip));

使用 ranges::actions 的排序工作正常,但 std::sort 无法编译并给出以下错误

/usr/include/c++/9.3.0/bits/stl_algobase.h:151: error: no matching function for call to ‘swap(concepts::return_t<ranges::common_pair<int&, double&>, void>, concepts::return_t<ranges::common_pair<int&, double&>, void>)’
  151 |       swap(*__a, *__b);
      |       ~~~~^~~~~~~~~~~~

为什么会这样?

我也试过同时删除两个容器中的元素。 ranges::actions::unique 未编译并出现以下错误:

/home/jjcasmar/projects/cpfsofaplugin/src/CPFSofaPlugin/minimalExample.cpp:27: error: no match for call to ‘(const ranges::actions::action_closure<ranges::actions::unique_fn>) (ranges::zip_view<ranges::ref_view<std::vector<int, std::allocator<int> > >, ranges::ref_view<std::vector<double, std::allocator<double> > > >&)’
   27 |     ranges::actions::unique(v1Andv2);
      |                                    ^

但是 auto lastIt = std::unique(std::begin(v1Andv2), std::end(v1Andv2)) 编译查找,虽然我不知道如何让 zip 的内部迭代器能够擦除结束元素。

我真的不明白这是如何在幕后工作的,以及为什么在某些情况下标准算法可以正常工作但在某些情况下却不能。有人可以解释一下吗?

看类型:

auto zip = ranges::views::zip(v1, v2);
// ranges::zip_view<
//   ranges::ref_view<std::vector<int>>
//   ranges::ref_view<std::vector<char>>
// >

auto begin = std::begin(zip);
// ranges::basic_iterator<
//   ranges::iter_zip_with_view<
//     ranges::detail::indirect_zip_fn_,
//     ranges::ref_view<std::vector<int>>,
//     ranges::ref_view<std::vector<char>>
//   >::cursor<false>
// >

using traits = std::iterator_traits<decltype(begin)>;
static_assert(std::is_same_v<traits::value_type, std::pair<int, char>>);
static_assert(std::is_same_v<traits::reference, ranges::common_pair<int&, char&>>);

value_type 类型是 std::pair 值。 reference 类型是 ranges::common_pair 个引用。

std::sort 使用 std::iter_swap,这是根据取消引用迭代器和调用 std::swap 指定的。所以 std::sort 将尝试交换两个 ranges::common_pair 的引用。另一方面,ranges::actions::sort 使用定制的 ranges::iter_swap 来处理引用对和元组。

引用对和元组 are/were 第二 class 标准库中的公民。

ranges::actions::unique需要一个可擦除范围,显然这不满足。

已添加

range-v3 的文档稀疏。要查找上述信息,当然可以查看 range-v3 的源代码,在 godbolt.org 上进行快速实验(range-v3 是一个可用的库),以及 "standard" C++ 技巧来找到变量的类型(例如,调用已声明但未定义的函数模板,将变量的类型作为模板参数,并查看调用了哪个实例化)。

要对 unique 进行更多评论,ranges::action::unique 不是 return 迭代器。它擦除非唯一元素和 return 范围(参见 the source)。在被省略的编译器错误的一部分中,错误引用了范围不可擦除的事实(埋在一个巨大的错误中)。

ranges::unique returns 是一个迭代器,可以无错调用。这是一个basic_iterator<...>。一种选择是使用 ranges::distance 查找与 begin zip 迭代器的距离,并使用它来获取底层迭代器:

auto zip_uniq_iter = ranges::unique(zip);
auto first_uniq_iter = std::next(v1.begin(), ranges::distance(ranges::begin(zip), zip_uniq_iter));

您不能对视图使用 std::sort。 但是您可以将视图转换为矢量,然后它就可以工作了: https://godbolt.org/z/_FvCdD

我可以推荐以下网站以获取有关范围的更多信息:

https://www.walletfox.com/course/quickref_range_v3.php https://mariusbancila.ro/blog/2019/01/20/cpp-code-samples-before-and-after-ranges/