I wanted to create a deep_flatten
function template that would produce a range
of elements that are deeply join
ed. For example, if we take into account only nested std::vector
s, I can have:
template <typename T>
struct is_vector : public std::false_type { };
template <typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type { };
template <typename T>
auto deepFlatten(const std::vector<std::vector<T>>& vec) {
using namespace std::ranges;
if constexpr (is_vector<T>::value) {
auto range = vec | views::join;
return deepFlatten(std::vector(range.begin(), range.end()));
} else {
auto range = vec | views::join;
return std::vector(range.begin(), range.end());
}
}
This enables me to do:
std::vector<std::vector<std::vector<int>>> nested_vectors = {
{{1, 2, 3}, {4, 5}, {6}},
{{7}, {8, 9}, {10, 11, 12}},
{{13}}
};
std::ranges::copy(
deep_flatten(nested_vectors),
std::ostream_iterator<int>(std::cout, " ")
);
which prints into the console the following text, as expected:
1 2 3 4 5 6 7 8 9 10 11 12 13
But, I don't like this solution that much. Not only it's inefficient (creating a number of temporary vectors), but it also works only with std::vector
s. I figured that I could use some more of c++20 magic and use std::ranges::range
concept:
namespace rng {
template <std::ranges::range Rng>
auto deep_flatten(Rng&& rng) {
using namespace std::ranges;
if constexpr (range<Rng>) {
return deep_flatten(rng | views::join);
} else {
return rng | views::join;
}
}
}
This seemed to me pretty straightforward - we have a std::ranges::range
and we inspect it's value type. Depending on whether it's a nested range, we recurse or simply return join
ed elements.
Sadly, it doesn't work. After trying to run:
int main() {
std::set<std::vector<std::list<int>>> nested_ranges = {
{{1, 2, 3}, {4, 5}, {6}},
{{7}, {8, 9}, {10, 11, 12}},
{{13}}
};
std::ranges::copy(
rng::deep_flatten(nested_ranges),
std::ostream_iterator<int>(std::cout, " ")
);
}
I get an error saying that:
In instantiation of 'auto rng::deep_flatten(Rng&&) [with Rng = std::ranges::join_view<std::ranges::ref_view<std::set<std::vector<std::__cxx11::list<int> > > > >]': required from 'auto rng::deep_flatten(Rng&&) [with Rng = std::set<std::vector<std::__cxx11::list<int> > >&]' required from here error: use of 'auto rng::deep_flatten(Rng&&) [with Rng = std::ranges::join_view<std::ranges::ref_view<std::set<std::vector<std::__cxx11::list<int> > > > >]' before deduction of 'auto' 39 | return deep_flatten(rng | views::join); | ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
Having researched similar problems, I cannot really get why the error appears here.
I am using gcc version 10.1.0 (Rev3, Built by MSYS2 project)