Consider the following snippet:
// included, for exposition only
SomeData buzz(std::string_view);
constexpr auto foo(std::string_view v) {
using namespace std::string_view_literals;
return v
| std::views::split(";"sv)
// `std::string_view` constructor from Range is explicit, so this line is necessary
| std::views::transform([](const auto& s) { return std::string_view(s); })
| std::views::transform(buzz)
;
}
Ideally I'd have only the transformations required by the actual algorithm without working around the language's/Standard Library's limitations:
constexpr auto bar(std::string_view v) {
using namespace std::string_view_literals;
// To be used as a chainable range closure more code is required
constexpr auto split{[](std::string_view v, std::string_view delim){
return v | std::views::split(delim) | std::views::transform([](const auto& v){ return std::string_view(v); });
}};
return split(v, ";"sv)
| std::views::transform(buzz);
}
Does the Standard Library provide a view adaptor that splits a std::string_view into std::string_view elements without the extra transform, or is a custom wrapper unavoidable?
There is no such custom adaptor. But it's easy enough to write your own — which also the advantage that you can make it work with string literals. It's not that much boilier plate:
struct SplitClosure : std::ranges::range_adaptor_closure<SplitClosure> {
std::string_view delim;
explicit constexpr SplitClosure(std::string_view delim) : delim(delim) { }
template <std::ranges::viewable_range R>
constexpr auto operator()(R&& r) const {
return r | std::views::split(delim)
| std::views::transform([](auto&& r){
return std::string_view(r);
});
}
};
struct SplitAdaptor {
constexpr auto operator()(std::string_view s) const {
return SplitClosure(s);
}
};
inline constexpr SplitAdaptor split2;
That makes s | split2(" ") work (in the sense that we're splitting on a space, not a space and null terminator, in addition to s | split2(" "sv).
If you copy the adaptor implementation from P2387R3, this could be:
inline constexpr adaptor split2 =
[]<std::ranges::viewable_range R>(R&& r, std::string_view delim){
return (R&&)r
| std::views::split(delim)
| std::views::transform([](auto&& sub){
return std::string_view(sub);
});
};
Barry's answer is predictably great, but since you are commenting for a specific clarification...
Your code:
return v
| std::views::split(";"sv)
// `std::string_view` constructor from Range is explicit, so this line is necessary
| std::views::transform([](const auto& s) { return std::string_view(s); })
| std::views::transform([](std::string_view v) {
// Some further logic here
return v;
})
;
is functionally identical to:
return v
| std::views::split(";"sv)
| std::views::transform([](auto&& range) {
std::string_view v{range}
// Some further logic here
return v;
})
;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With