I am trying to convert a vector to a string and join with C 20(MSVC on visual studio 19 v142, c latest).
std::vector<std::string> strings = { "1s","2s" };
auto view1 = strings | std::views::transform([](std::string num) {return std::format("{} ", num); }); // okay
auto view2 = strings | std::views::join; // okay
auto view3 = strings | std::views::transform([](std::string num) {return std::format("{} ", num); }) | std::views::join; // Compiler error
//error C2678: binary '|': no operator found which takes a left-hand operand of type 'std::ranges::transform_view<std::ranges::ref_view<std::vector<std::string,std::allocator<std::string>>>,_Ty>' "
I know I can do it elegantly with actions
in range-v3, but I am not supposed to use it.
Is there a way to do it, by just using C 20 standard library?
CodePudding user response:
In the original design of views::join
, it didn't just join any range of ranges, it had to be a range of reference to ranges.
This worked:
auto view2 = strings | views::join; // okay
Because strings
is a range of string&
(which is a reference to a range).
But this did not:
auto view3 = strings | views::transform([](std::string num) {
return std::format("{} ", num)
})
| views::join; // Compiler error
because now we're join
ing a range of string
(specifically, prvalue strings, which is what your function returns). That violated the constraints. range-v3's views::join
has the same constraint.
However, it is a fairly common use-case to be able to do this. So a recent defect against C 20 was to change the design of views::join
to allow it to join any range of ranges. That's P2328. libstdc (gcc's standard library) already implements this, MSVC does not yet, nor does range-v3 (though I have a PR open). So the official C 20 answer is: what you're doing is correct, your library just hasn't implemented it yet.
Now, the range-v3 answer is to introduce a caching layer:
auto view3 = strings | rv::transform([](std::string num) {
return std::format("{} ", num)
})
| rv::cache1
| rv::join;
What cache1
does is cache one element at a time, which turns our range of prvalue string
into a range of lvalue string
such that join
can successfully join it. The P2328 design is to basically incorporate this internally into join
itself so that you don't have to do it manually. But until that happens, you can do the above using range-v3. There is no cache1
in C 20 and won't be in C 23 either.
Or you can use Casey's hack. But also don't.