Подтвердить что ты не робот

Почему std::string не содержит метод split()?

Я предполагаю, что существует законная причина класса std::string, не содержащего метод разделения строки на основе разделителя. При наличии boost::split (и других хороших решений, размещенных здесь: Разделить строку на С++), похоже, что можно создать общее решение. Так почему же он не попал в стандартную библиотеку?

4b9b3361

Ответ 1

Вам может быть интересно прочитать следующее: std:: split(): алгоритм разделения строк

Разделение строк на подстроки является общей задачей в большинстве языки программирования общего назначения, а С++ не является исключением. когда возникает необходимость, программисты должны искать существующее решение или написать один из своих. Типичное решение может выглядеть как следующее:

std::vector<std::string> my_split(const std::string& text, const std::string& delimiter);

Также проверьте эту тему на Quora: Есть ли какие-то особые причины, почему мощный язык, такой как С++ a), не имеет функции split() (в любом STL, а не Boost и т.д.). Копирование с источника ниже:

Рассмотрим С++ 03. Я думаю, причина, по которой нет строки функция расщепления в С++ 03 заключается в том, что не ясно, как написать один который прост в использовании и совместим со всеми стандартами С++ принципы дизайна библиотеки.

Во-первых, как вы возвращаете произвольное количество строк из функционировать? По значению? Нет, слишком много копий. Вы можете вернуть контейнер может быть, есть ссылки на подобные объекты? По-прежнему потенциально линейный количество копий, и у него есть свои проблемы: вам придется выполнить некоторое копирование вручную после возвращения функции, потому что в противном случае ваша исходная строка может быть уничтожена или изменена.

ОК, поэтому лучше всего использовать контейнер для контейнера. Эта согласуется с тем, как стандартная библиотека С++ работает в целом. Какой контейнер? В то время как std::vector - очевидный выбор, нет стандартные библиотечные алгоритмы, которые работают только на векторах. Стандарт библиотечные алгоритмы вообще не понимают контейнеры, и это дизайн. Все, что они видят, являются итераторами.

// header
template <class Iterator>
size_t split(const string str&, Iterator out); // container agnostic
// typical usage
std::string str;
std::vector<std::string> vec;
std::split(str, std::back_inserter(vec));

(Функция возвращает количество фрагментов строки.)

Но это все еще недостаточно общего. Конечно, это по крайней мере выглядело бы например:

template <class Iterator, class charT, class Traits, class Alloc>
size_t split(const basic_string<charT, Traits, Alloc>& str, Iterator out);

Но на самом деле, подождите, почему функция должна работать только std:: basic_string объекты? Возможно, мы хотим разделить содержимое входной файл или подстроку std::string. Мы не должны сначала скопируйте все содержимое в строку, а затем вызовите split(). Это будет использовать в два раза больше памяти по мере необходимости. Разумеется, ввод split() должен быть диапазоном итератора.

template <class InputIterator, class OutputIterator>
size_t split(InputIterator begin, InputIterator end, OutputIterator out);

Но теперь у нас есть новая проблема: как могла такая функция знать, что вид объекта для вставки в выходной итератор? Один из способов, конечно, заключается в том, чтобы принудительно указать контейнер, если задание функции:

// header
template <class String, class InputIterator, class OutputIterator>
size_t split(InputIterator begin, InputIterator end, OutputIterator out) {
    // ...
    // *out++ = String(...);
    // ...
}
// typical usage
std::string str;
std::vector<std::string> vec;
std::split<std::string>(str.begin(), str.end(), std::back_inserter(vec));

Вот еще одна проблема: каждый раз, когда мы вставляем строку в вывод итератора, мы должны создать временную строку, которая получает push_back() ред. Компилятор не может удалить копию в целом, поэтому здесь происходит ненужное копирование. Если вы пишете функция расщепления себя с помощью вектора, вы можете просто push_back() пустую строку, а затем вызывать push_back() каждый раз, когда вы хотите вставьте новый символ в одну из выходных строк. Этот подход плохо взаимодействует с общим характером стандартной библиотеки алгоритмов. Это потребует, чтобы Итератор был как минимум переместите итератор, поэтому забудьте о вставке в поток или используя std:: back_insert_iterator, как в примере. Это также требует тип назначения, подлежащий изменению. Что делать, если вы хотите, чтобы пользовательский неизменяемый тип строки, и вы действительно хотите, чтобы это было построены все сразу и скопированы?

Кроме того, это взаимодействует с характером входного диапазона. Если Итераторы ввода не являются даже итераторами вперед, тогда split() будет иметь нет выбора, кроме как push_back по одному символу за раз. Но если они на наименее продвинутые итераторы, тогда split() может вызывать конструктор диапазона, который, вероятно, будет более эффективным. Но любой вариант зависит от операции, поддерживаемые типом вывода. (Должен ли я называть диапазон конструктор и сделать копию, или вставить пустую строку, а затем вызвать функция вставки на нем?)

Основная проблема заключается в том, что вы не можете написать хороший общий Функция std:: split: вам нужно будет сделать некоторые компромиссы между общности и эффективности. Функция разделения не является трудной для записи, поэтому если вам это нужно, вы можете написать один, который эффективен для с которыми вы работаете. Стандартная библиотека С++ не например, делать компромиссы, как это, в общем: большинство стандартных библиотек алгоритмы являются очень общими и высокоэффективными.

Некоторые из этих проблем решаются путем введения семантика в С++ 11. Но, конечно, всем, кому нужно разделить строки, уже написаны свои собственные функции разделения, которые эффективны для их собственные цели, поэтому, возможно, просто не было приоритетом вводить стандартный.

Сравните это с чем-то вроде std:: map: трудно писать, но это довольно ясно, как должен выглядеть интерфейс. Что делает это хороший кандидат для стандартной библиотеки С++.