使用std::views::split拆分std::string_view时如何减少代码冗余?
这个问题问得太戳中痛点了!相信不少人用std::views::split处理std::string_view的时候,都被那层额外的transform烦到——明明拆分的是字符串视图,结果出来的元素却是个子范围,还得手动转成std::string_view,重复代码写得人头疼。
先给你一个明确的结论:截至C++23,标准库并没有直接提供能把std::string_view拆分成std::string_view元素的视图适配器,所以你没法完全绕过那个额外的transform步骤,自定义包装器确实是目前的最优解。
为什么标准库要这么设计呢?其实是因为std::views::split是个通用工具,它的目标是支持所有符合输入范围要求的类型,而不只是std::string_view。它返回的元素是原范围的子范围(对应string_view的话就是std::ranges::subrange),而std::string_view的构造函数又是explicit的,没法自动从子范围转换过去,这就导致必须加那层显式的transform来完成类型转换。
不过你自己写的那个闭包包装思路已经非常棒了!把拆分+转换的逻辑封装成一个可复用的函数,就能避免每次都写重复的代码。我自己平时处理这类场景的时候,也会封装类似的工具,甚至会把它写成更贴近标准库风格的自定义视图适配器,比如:
struct split_to_string_view_fn { template <std::ranges::input_range R, std::ranges::forward_range Delim> requires std::convertible_to<std::ranges::range_value_t<R>, char> auto operator()(R&& r, Delim&& delim) const { return std::forward<R>(r) | std::views::split(std::forward<Delim>(delim)) | std::views::transform([](auto&& subrange) { return std::string_view(std::ranges::begin(subrange), std::ranges::end(subrange)); }); } }; inline constexpr split_to_string_view_fn split_to_string_view{};
用的时候就可以像标准库适配器那样串联,代码看起来更清爽:
constexpr auto bar(std::string_view v) { using namespace std::string_view_literals; return split_to_string_view(v, ";"sv) | std::views::transform(buzz); }
当然,如果你只是在单个函数里用,你写的那个闭包形式已经足够简洁了,没必要搞这么复杂的适配器。
总的来说,虽然标准库目前没有提供这种“开箱即用”的适配,但通过简单的自定义封装,完全可以把冗余代码降到最低,不用每次都重复写那串transform。
内容来源于stack exchange

