Reputation: 1675
I am trying to understand how ranges::views::zip works in range-v3. I understand that it is a range that allows to iterate on several ranges in one single loop by creating a tuple of the elements in different ranges.
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));
The sort using ranges::actions
works fine but std::sort
doesnt compile and gives the following error
/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);
| ~~~~^~~~~~~~~~~~
Why is this happening?
I have also tried to remove elements in both containers at the same time. ranges::actions::unique
doesn't compile with the following error:
/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);
| ^
but auto lastIt = std::unique(std::begin(v1Andv2), std::end(v1Andv2))
compiles find, although I dont know how to get the internal iterators of the zip to be able to erase past the end elements.
I dont really understand how this works under the hood and why in some cases std algorithms work fine but in some cases it doesn't. Can someone give some explanation about this?
Upvotes: 0
Views: 4724
Reputation: 661
You cannot use std::sort on views. But you can transform your view to a vector and then it works: https://godbolt.org/z/_FvCdD
I can recommend the following sites for more information on ranges:
https://www.walletfox.com/course/quickref_range_v3.php https://mariusbancila.ro/blog/2019/01/20/cpp-code-samples-before-and-after-ranges/
Upvotes: 0
Reputation: 7528
Look at the types:
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&>>);
The value_type
type is a std::pair
of values. The reference
type is a ranges::common_pair
of references.
std::sort
uses std::iter_swap
which is specified in terms of dereferencing the iterators and calling std::swap
. So std::sort
will try to swap two ranges::common_pair
of references. On the other hand, ranges::actions::sort
uses ranges::iter_swap
which is customized to handle pairs and tuples of references.
Pairs and tuples of references are/were second class citizens in the standard library.
ranges::actions::unique
requires an erasable range which evidently this does not satisfy.
Added
The documentation for range-v3 is sparse. To find information such as the above, there is of course looking at the source for range-v3, quick experimentation on godbolt.org (range-v3 is an available library), and "standard" C++ tricks to find the type of a variable (e.g., calling a function template which is declared but not defined, with the variable's type as the template argument, and seeing which instantiation is called).
To comment more on unique
, ranges::action::unique
does not return an iterator. It erases the non-unique elements and returns a range (see the source). In part of the compiler error which was omitted, the error references the fact that the range is not erasable (buried in a huge error).
ranges::unique
returns an iterator and can be called without error. This is a basic_iterator<...>
. One option is to use ranges::distance
to find the distance from the begin
zip iterator and use this to get the underlying iterator:
auto zip_uniq_iter = ranges::unique(zip);
auto first_uniq_iter = std::next(v1.begin(), ranges::distance(ranges::begin(zip), zip_uniq_iter));
Upvotes: 5