Каковы лучшие практики для разработки шаблонов функций для работы со строковыми / символьными аргументами? - PullRequest
0 голосов
/ 27 мая 2018

Я хотел бы написать простую строку split функцию.

Функция должна взять один std::basic_string и разделитель (возможно, CharT или std::basic_string) и поместить результатв ContainerT.

Моя первая попытка

template <typename StringT, typename DelimiterT, typename ContainerT>
void split(
    const StringT &str, const DelimiterT &delimiters, ContainerT &conts) {
    conts.clear();
    std::size_t start = 0, end;
    std::size_t len = delimiters.size();
    while ((end = str.find(delimiters, start)) != StringT::npos) {
        if (end - start) {
            conts.emplace_back(str, start, end - start);
        }
        start = end + len;
    }

    if (start != StringT::npos && start < str.size()) {
        conts.emplace_back(str, start, str.size() - start);
    }
}

Моя конечная цель - расширить эту функцию для достижения:

  1. Окончательные результаты всегдаstd::basic_string<CharT> положить в некоторый conts.
  2. Первый аргумент str может быть std::basic_string<CharT>, const CharT* или строковым литералом.
  3. Второй аргумент delimiter может бытьchar или std::basic_string<CharT> / const CharT* / строковый литерал, означающий, что длина разделителя больше 1, например, разделение aaa,,bbb,c с ,, дает aaa/bbb,c.
  4. Третьим аргументом может быть любой контейнер последовательности из STL.

Так как обычно в C ++ используются современные строки, 2 может быть std::basic_string<CharT> только для упрощения.

Учитывая, чтоИнтересно, что функция (шаблон) может быть перегружена

  1. По крайней мере, сколько функций мне понадобится в этой ситуации?
  2. И каков наилучший способ разработки таких функций (Как написать более общие функции)?Например, возможно, чтобы вышеуказанная функция работала с разделителем const CharT*, строка std::size_t len = delimiters.size(); должна быть изменена на некоторое std::distance(...)?

Обновление:

Возрождениедобавлен обзор кода здесь .

Ответы [ 3 ]

0 голосов
/ 27 мая 2018

Я бы предложил использовать два параметра шаблона, один для входной строки и один для выходного контейнера, потому что почти во всех случаях входная строка, разделитель и выходной контейнер будут одного типа, поэтому вы можете определить свою функцию примерно так-

template<typename charT, typename Container)
void split(const std::basic_string<charT> input,
    const charT deliminator,
    Container<std::basic_string<chart>> &cont)

для 2-го сценария ваш разделитель может иметь тип std::basic_string<charT>.

0 голосов
/ 27 мая 2018

Вы можете использовать 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.

0 голосов
/ 27 мая 2018

Создайте basic_string_view из входных строк, затем оперируйте ими.basic_string_view имеет явный конструктор, принимающий char*, а basic_string имеет оператор приведения к basic_string_view.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...