Вы можете использовать std::string_view
как для разделения текста, так и для разделителя.Кроме того, вы можете использовать параметр шаблона шаблона, чтобы выбрать тип элементов в результате:
template<typename Char, template<typename> class Container, typename String>
Container<String> split_impl(std::basic_string_view<Char> text, std::basic_string_view<Char> delim)
{
Container<String> result;
//...
result.push_back(String(text.substr(start, count)));
//...
return result;
}
template<template<typename> class Container, typename String = std::string_view>
Container<String> split(std::string_view text, std::string_view delim)
{ return split_impl<char, Container, String>(text, delim); }
template<template<typename> class Container, typename String = std::u16string_view>
Container<String> split(std::u16string_view text, std::u16string_view delim)
{ return split_impl<char16_t, Container, String>(text, delim); }
Таким образом, его можно использовать с std::string
, std::string_view
и const char*
без избыточных выделений:
// vector of std::string_view objects:
auto words_1 = split<std::vector>("hello world", " ");
// list of std::string objects:
auto words_2 = split<std::list, std::string>(std::string("hello world"), " ");
// vector of std::u16string_view objects:
auto words_3 = split<std::vector>(u"hello world", u" ");
Редактировать: добавлены перегрузки для char
и char16_t
Редактировать 2
В приведенном выше коде,split_impl
выполняет реальную работу.split
перегрузки предоставляются только для упрощения пользовательского кода, так что вам не нужно явно указывать тип символа, который будет использоваться.Это было бы необходимо без перегрузок, потому что компилятор не может вывести Char
, когда тип параметра равен basic_string_view
, и вы передаете аргумент другого типа (например, const char*
или std::wstring
).В общем, я думаю, что это не большая проблема - возможно, вы хотите иметь четыре перегрузки (char
, char16_t
, char32_t
, wchar_t
), если не меньше.
Однакодля полноты, вот альтернатива, которая не использует перегрузки:
template<typename ContainerT, typename TextT, typename DelimT>
ContainerT split(const TextT& text, const DelimT& delim)
{
using CharT = std::remove_reference_t<decltype(text[0])>;
std::basic_string_view<CharT> textView(text);
std::basic_string_view<CharT> delimView(delim);
ContainerT result;
// actual implementation, but using textView and delimView instead of text and delim
result.push_back(textView.substr(start, count));
return result;
}
// usage:
auto words = split<std::vector<std::string_view>>("some text", " ");
При таком подходе вы не можете использовать значение по умолчанию String
параметра шаблона, как указано выше (поскольку оно должно зависеть от TextT
тип).По этой причине я его убрал.Кроме того, этот код предполагает, что text
и delim
используют один и тот же тип символов и могут быть преобразованы в basic_string_view
.
Лично я предпочитаю версию 1. Он не использует типы шаблонов для параметров функции, что IMHO лучше, так как это дает вызывающему абоненту лучшее представление о том, что должно быть передано. Другими словами, интерфейс первого split
лучше определен.Кроме того, как отмечалось выше, я не рассматриваю необходимость добавления четырех перегрузок split
.