std::ranges::split_view
работает, беря диапазон, который должен быть разделен в паре с разделителем.
Однако указанный разделитель определен довольно своеобразно — он должен быть forward_range.
К счастью, стандарт позволяет использовать split_view
таким образом, что передается диапазон и один элемент. Примечательно, что это пример из стандарта:
string str{"the quick brown fox"};
for (auto word : views::split(str, ' ')) {
cout << string_view(word) << '*';
}
Как видите, мы можем передать ' '
в качестве разделителя. Насколько мне известно, это работает с использованием следующего конструктора:
template<forward_range R>
requires constructible_from<V, views::all_t<R>>
&& constructible_from<Pattern, single_view<range_value_t<R>>>
constexpr explicit split_view(R&& r, range_value_t<R> e);
Это позволяет нам передавать std::string
как диапазон R
и char
как разделитель e
. Предложение requires
гарантирует, что это будет работать, проверяя, является ли char
range_value_t
для std::string
(это так), и возможно ли создать std::ranges::single_view<char>
(потенциально всегда делегировать реализацию, которая предполагает, что разделитель является диапазоном). Творит чудеса.
Но что, если я хочу сильно настроить свое поведение разделения? Например, я хотел разбить по любому пробелу. По глупости я думал, что это сработает:
struct Whitespace {
auto operator==(char const c) const noexcept -> bool {
return std::isspace(static_cast<unsigned char>(c));
}
friend auto operator==(char const c, Whitespace const ws) noexcept -> bool {
return ws == c;
}
} whitespace;
auto main() -> int {
auto const text = std::string("3213 421 43 3 532 5 53 53 5 3535 5353");
namespace views = std::ranges::views;
auto numbers = text | views::split(whitespace);
}
Но Whitespace
— это тип, который не является диапазоном, из него нельзя создать std::ranges::single_view<char>
. И даже если бы это было так и если бы вы могли, у вас не было бы возможности сохранить его пользовательское поведение, учитывая тот факт, что упомянутый конструктор преобразует его в простой, старый char
.
Могу ли я как-то использовать std::views::split
с пользовательской логикой для разделения, скажем, диапазонов char
?
Нет. split_view
требует indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to>
, а ranges::equal_to
требует equality_comparable_with
(с небольшой оговоркой, здесь не уместной). Семантические требования этой концепции запрещают нетранзитивное «равенство» (т. Е. t1 == u
, t2 == u
, но t1 != t2
), которое необходимо для любого u
, которое соответствует более чем одному значению.
Из этого следует, что все попытки сделать такие вещи либо являются IFNDR, либо имеют неопределенное поведение.
Я думаю, вы хотите что-то более похожее на Range-v3 split_when