如何从 C++20 中的范围创建一个新字符串?

Rei*_*ica 5 c++ c++20

std::string从(另一个字符串的)转换视图创建 a 的最简洁和/或惯用的方法是什么?

我能想到的最好的方法是std::ranges::copy将视图和插入器用作参数,即使这样也只被 gcc(10.2 和主干)接受,但不接受 clang 11.x。

对于大多数ranges算法,我认为目的是operator|可以在函数的第一个参数是范围时使用,所以我想知道为什么第二个和恕我直言的更清洁变体不能编译(错误消息是没有copy找到匹配) ,并且是最终编译它的意图,还是什么?

我尝试了 gcc 10.2 和 gcc (11) 主干。Clang 也不编译第一种形式:(

#include <algorithm>
#include <cctype>
#include <iterator>
#include <ranges>
#include <string>

int main() {
    std::string original = "foo";
    std::string upper;

    // This compiles on gcc 10.2/11-trunk only
    std::ranges::copy(
        original 
            | std::ranges::views::transform([](char c){return std::toupper(c);}),
        std::back_inserter(upper)
    ); 

    // The below doesn't compile period
    original
        | std::ranges::views::transform([](char c){return std::toupper(c);})
        | std::ranges::copy(std::back_inserter(upper));
}
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 5

对于大多数范围算法,我认为目的是operator|只要函数的第一个参数是范围就可以使用

这不太对。operator|仅适用于第一个参数是一个范围并且结果是一个范围的那些算法。

transformfiltertake等都是取一个范围并返回一个范围的算法。更具体地说,他们采用 aviewable_range并返回 a view

但是对于不返回范围的所有其他算法 - maxfind_ifany_of等 - 没有|其他选择。所以这:

    original
        | std::ranges::views::transform([](char c){return std::toupper(c);})
        | std::ranges::copy(std::back_inserter(upper));
Run Code Online (Sandbox Code Playgroud)

甚至不打算工作,因为copy不是返回 a 的算法之一view,所以它没有得到|支持。管道重写运算符提案 ( P2011 )的动机之一是能够使用此类功能(无需大量库机制使其工作所需)。


另一方面,这:

    std::ranges::copy(
        original 
            | std::ranges::views::transform([](char c){return std::toupper(c);}),
        std::back_inserter(upper)
    ); 
Run Code Online (Sandbox Code Playgroud)

是完全有效的代码,旨在工作。这是由于 clang 过于急切地检查某些概念造成的(请参阅错误 47509)。我认为还有另一个错误报告,只是暂时找不到。


最终ranges::to将采用 from range-v3,因此预期的惯用结构将是:

    std::string upper = 
        original 
            | std::ranges::views::transform([](char c){return std::toupper(c);}),
            | std::ranges::to<std::string>();
Run Code Online (Sandbox Code Playgroud)

在此之前,您可以将 range-v3 用于to.