I'm trying to check if all values in nested vector are same/unique. I wrote this program (simplified, but same error):
#include <algorithm>
#include <iostream>
#include <ranges>
#include <vector>
auto main() -> int
{
std::vector<std::vector<int>> vectors_of_ints {
{1, 2, 3},
{4, 5, 6}
};
auto get_vectors = [](const std::vector<int>& v) { return v; };
auto get_ints = [](const int& i) { return i; };
auto all_ints = vectors_of_ints
| std::views::transform(get_vectors)
| std::views::join
| std::views::transform(get_ints);
auto status = std::ranges::adjacent_find(all_ints, std::not_equal_to{}) != std::ranges::end(all_ints);
return status;
}
but I'm getting this error:
<source>:22:44: error: no match for call to '(const std::ranges::__adjacent_find_fn) (std::ranges::transform_view<std::ranges::join_view<std::ranges::transform_view<std::ranges::ref_view<std::vector<std::vector<int> > >, main()::<lambda(const std::vector<int>&)> > >, main()::<lambda(const int&)> >&, std::not_equal_to<void>)'
22 | std::cout << std::ranges::adjacent_find(all_ints, std::not_equal_to{}) != std::ranges::end(all_ints);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[...] // too long to post
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/ranges_base.h:594:13: required for the satisfaction of 'forward_range<_Range>' [with _Range = std::ranges::transform_view<std::ranges::join_view<std::ranges::transform_view<std::ranges::ref_view<std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > > >, main::._anon_112> >, main::._anon_113>&]
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/concepts:67:28: note: 'std::forward_iterator_tag' is not a base of 'std::input_iterator_tag'
67 | concept derived_from = __is_base_of(_Base, _Derived)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compiler returned: 1
Which concludes with: std::forward_iterator_tag is not a base of std::input_iterator_tag
.
I would like to understand the problem I'm having here and how to fix that.
std::ranges__adjacent_find
requires a forward_range
as input, but your all_ints
is only an input_range
. From cppreference:
join_view models forward_range when:
ranges::range_reference_t<V>
is a reference type, andV
andranges::range_reference_t<V>
each model forward_range.
std::ranges::range_reference_t<Range>
is the return type of an element of a range of type Range
. Because you transform vector_of_ints
with your get_vectors
, an element of the resulting range is std::vector
and thus std::ranges::range_reference_t
is not of reference type (See demo ). Note this happens only because auto follows the template argument deduction rules and does not deduce reference. If get_vectors
to return a reference to vector
instead, you can use decltype(auto)
:
auto get_vectors = [](const std::vector<int>& v) -> decltype(auto) { return v; };
or specify the return type of the lambda explicitly.
Or the simple
auto all_ints = vectors_of_ints | std::views::join;
works.
If you really want to copy the contents, consider creating a new vector from all_ints
:
std::vector<int> all_ints_copy(all_ints.begin(), all_ints.end());
In C++23 std::ranges::to
simplifies this process and mitigates all copies to the end:
auto all_ints_copy_to = vectors_of_ints
| std::views::join
| std::ranges::to<std::vector<int>>();