使用 std::views 在 C++20 中将 std::vector<string> 转换为 std::string

Convert std::vector<string> to just a std::string in C++20 using std::views

我正在尝试将向量转换为字符串并与 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>' "

我知道我可以在 range-v3 中使用 actions 优雅地完成它,但我不应该使用它。

有没有办法只使用 C++20 标准库来做到这一点?

views::join最初的设计中,并不是只是加入any个ranges范围,它必须是ranges对ranges的引用

这有效:

auto view2 = strings | views::join; // okay

因为stringsstring&的范围(这是对范围的引用)。

但这并没有:

auto view3 = strings | views::transform([](std::string num) {
                           return std::format("{} ", num)
                       })
                     | views::join; // Compiler error  

因为现在我们 join 范围 string(具体来说,纯右值字符串,这是你的函数 returns)。那违反了约束。 range-v3 的 views::join 具有相同的约束。


不过,这是一个相当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)。所以官方的 C++20 答案是:你所做的是正确的,只是你的库还没有实现它。


现在,range-v3 答案是引入一个缓存层:

auto view3 = strings | rv::transform([](std::string num) {
                           return std::format("{} ", num)
                       })
                     | rv::cache1
                     | rv::join;

cache1所做的是一次缓存一个元素,这将我们的纯右值范围string变成了左值范围string,这样join就可以成功加入它。 P2328 的设计基本上是将其内部合并到 join 本身,这样您就不必手动执行。但在此之前,您可以使用 range-v3 执行上述操作。 C++20 中没有 cache1,C++23 中也不会有。

或者您可以使用 Casey's hack。但也不要。