I ran into this problem while trying to make a powerset function using the std::views library.
auto set = sv::repeat(0)
| sv::take_while([&mask](...) { return mask > 0; })
| sv::transform([s, &mask, idx = 0](...) mutable {
return idx = std::countr_zero(mask & ~(mask - 1)), mask &= mask - 1, s[idx]; });
auto vec = std::vector(set.begin(), set.end());
^__________ ^______________________
"*cannot deduce types*" "*no constructor found*" <- these are rough translations of verbose diagnostics
My question is, why doesn't this work? Normally I can construct vectors using views like this perfectly well, and I even do it later in the function. I tested the same thing, but this time I used a captureless and const lambda:
auto arr = std::array {1, 2, 3, 3, 4, 5, 6, 1, 2, 3};
auto arr_view = arr | std::views::take_while([](int i) { return i < 4; });
auto vec = std::vector(arr_view.begin(), arr_view.end());
and got identical error messages.
Stranger still, I tried using the std::ranges::move() function to do the same thing, and it worked for some reason!
This code compiles and runs as expected (both with the constructed view as an rvalue and stored in a variable):
auto arr_view = arr | std::views::take_while([](int i) { return i < 4; });
auto vec = std::vector<int>{};
std::ranges::move(arr_view, std::back_inserter(vec));
Initially, I thought this was because "arr_view" wasn't a sized range, but the same iterative approach of construction works for std::views::filter(), which is also not a sized range.
Can someone explain to me the reasoning behind this not working? Is it just me using (unstable) modern features?
set.begin()andset.end()return iterators of different types, i.e.begin()returns an iterator whileend()returns a sentinel. This is necessary fortake_while, because one does not know beforehand how long to take. the sentinel returned byend()will compare equal to the iterator, once the predicate oftake_whileevaluates to false.To construct a
std::vectorfrom a range with a sentinel there are different methods:views::commonmakes a view into a common view i.e. with different type iterators:std::vectors new constructor taking a range. unfortunately, it needs a second argument to allow for the correct constructor to be chosen thus an object ofstd::from_range_tneeds to be passed as the first argument.std::ranges::toto convert a range to a container:See Demo
Note: depending on your different methods might be implemented (e.g. in the demo clang trunk needs
-stdlib=libc++to compile with its own standard library)