1

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?

Sumit Jha
  • 1,601
  • 11
  • 18
  • 2
    When a program fails to compile, usually the compiler tells you why. Did you read the diagnostic message? – eerorika Sep 21 '21 at 14:08
  • The example program doesn't seem to match the question. The program converts integer to strings; not a vector to a string. – eerorika Sep 21 '21 at 14:14
  • AFAIK `std::format` is not supported yet by clang or gcc. And I think msvc has some problems with ranges. – Marek R Sep 21 '21 at 14:17
  • 2
    MSVC doesn't implement [P2328](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2328r1.html) yet. – Barry Sep 21 '21 at 14:19
  • Demo when dropping std::format: https://godbolt.org/z/xThqaY6Yd – Marek R Sep 21 '21 at 14:20
  • @eerorika I looked at the compiler error, and I know it will not work. I would like to know if there is some workaround other than using range-v3 library. – Sumit Jha Sep 21 '21 at 14:21
  • @SumitJha You knowing why it will not work isn't very useful for us. If you don't tell us why your program won't work, then how do you expect us to tell you how to work around it? – eerorika Sep 21 '21 at 14:24
  • @SumitJha you have forgotten to write which compiler you are using (probably msvc) its version, and platform. Note that providing error logs is good practice in general. "Doesn't work" is not a description of a problem, compiler logs are. – Marek R Sep 21 '21 at 14:24
  • @MarekR I am using MSVC c++latest and it supports format but the problem is not with format. I have updated the example. I am sorry, I will update the compiler. – Sumit Jha Sep 21 '21 at 14:27
  • @Barry I am able to use std::views::join with MSVC, I not sure what you meant sorry. – Sumit Jha Sep 21 '21 at 14:27
  • 1
    Please [edit] your question when adding vital information, so future reader do not have to go through comments to have full picture. – Marek R Sep 21 '21 at 14:28
  • @SumitJha note I written that gcc and clang do not support `std::format` and msvc do not support ranges join yet as Barry point out. So there is not compiler where your code works. C++20 is still to fresh to be used. – Marek R Sep 21 '21 at 14:30
  • @MarekR I didn't say MSVC doesn't support `join`. I said specifically that it doesn't implement *that paper*. – Barry Sep 21 '21 at 14:35
  • @Barry sorry wrong wording from my side. – Marek R Sep 21 '21 at 14:36
  • @Barry Sorry for misunderstanding. – Sumit Jha Sep 21 '21 at 14:38

1 Answers1

1

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.

Barry
  • 286,269
  • 29
  • 621
  • 977