Home > Back-end >  Convert std::vector<string> to just a std::string in C 20 using std::views
Convert std::vector<string> to just a std::string in C 20 using std::views

Time:09-22

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 joining 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.

  • Related