Есть строка, которую надо скопировать в std::string с приведением символов к верхнему регистру.
Как это сделать оптимальным способом?
Текущие варианты, которые не нравятся.
нулевой
устанавливаем строку в std::string и приводим символы к верхнему регистру. Нафига устанавливать данные, которые потом перезапишутся?
первый
делаем resize получателю и заменяем символы. не нравится resize — он установит значения символов, которые потом будут перезаписаны.
второй
делаем reserve и push_back-ом добавляем преобразованные символы. Не нравится push_back — он будет каждый раз пытаться резервировать место, которое и так уже есть.
Пока писал, на ум пришел шаблонный метод std::string::assign, которому передают хитрые итераторы, которые будут преобразовывать поток символов.
Как-то так:
char upper_char_op(char c) {...}
//...
transform_range r(source_begin, source_end, upper_char_op);
// оператор * будет пропускать символы через upper_char_op
std::string s(r.begin(), r.end());
То есть, в итоге, вопрос такой — в STL есть что-то подобное transform_range или нужно изобретать свой велосипед?
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: std::string, копирование с трансформацией символов
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>То есть, в итоге, вопрос такой — в STL есть что-то подобное transform_range или нужно изобретать свой велосипед? Тут, первая ссылка в гугле
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>То есть, в итоге, вопрос такой — в STL есть что-то подобное transform_range или нужно изобретать свой велосипед?
Учитывая, что в бусте есть transform_iterator, который делает то, что вам нужно. Я предположу, что этого нет в стандарте.
#include <iostream>
#include <string>
#include <cctype>
#include <boost/iterator/transform_iterator.hpp>
int main()
{
const std::string in = "str";
auto beg = boost::make_transform_iterator(in.begin(), toupper);
auto end = boost::make_transform_iterator(in.end(), toupper);
std::string out(beg, end);
std::cout << out << std::endl;
const char in2[] = "raw";
auto beg2 = boost::make_transform_iterator(in2, toupper);
auto end2 = boost::make_transform_iterator(in2 + 3, toupper);
std::string out2(beg2, end2);
std::cout << out2 << std::endl;
return 0;
}
Re: std::string, копирование с трансформацией символов
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Текущие варианты, которые не нравятся.
Чуда не случится. В итоге в варианте с transform будет то же самое: либо создание буфера через reserve либо добавление по одному символу через push_back, которые оба тебе почему-то не нравятся.
--
RedApe
Re: std::string, копирование с трансформацией символов
Здравствуйте, Kernan, Вы писали:
K>std::string str_toupper(std::string s)
А аргумент s откуда возьмётся? Его скопируют из старой строки.
Это именно нулевой вариант топикстартера, с установкой и последующей переустановкой символов, два прохода, очень медленно.
Re[2]: std::string, копирование с трансформацией символов
Здравствуйте, vadfromnu, Вы писали:
V>Учитывая, что в бусте есть transform_iterator, который делает то, что вам нужно.
Он, конечно, делает. Но вся радость от него — он заметает всю лишнюю работу под шкаф, чтобы нежный программист на неё вдруг не наткнулся. Boost — вообще не про скорость, он про высокие материи и глубокий внутренний мир.
V>Я предположу, что этого нет в стандарте.
Увы, если от работы нельзя избавиться по стандарту, то написанные над этим стандартом библиотеки никак в этом не помогут.
По-хорошему, после резерва (в реальности — malloc/realloc) нужно было бы тупо установить текущий size равным capacity. Но на это стандартизаторы пойтить никак не могут, вдруг где-то что-то недоинициализируется. И так не только со строками, но и с массивами. Обходящая это библиотека могла бы тупо, зная смещения переменных относительно объекта в данной версии компилятора и стандартной библиотеки, прочитать и записать по соответствующим адресам. Но за это она сразу станет нерукопожатной, а boost — не такой, он же, фактически, главный резерв партии, поставщик новых фич в стандарт.
В общем, стандартизаторы давно уже отменили правило Трупа Страуса "чем не пользуешься — за то не платишь".
В iostream и вокруг него это гораздо хуже, там за нафиг не нужные (и не используемые) локали приходится платить очень дорого. Ввод массива целых чисел из текстового файла в лучшем случае на полтора порядка дороже их ручного вычитывания.
А недавно они ещё с односвязными списками прикололись, от большого ума потребовали константный по времени метод size. В результате split стал линейным, от чего сам Степанов офигел.
Re: std::string, копирование с трансформацией символов
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Не нравится push_back — он будет каждый раз пытаться резервировать место, которое и так уже есть.
Не пытаться, а каждый раз проверять. Для больших строк — самый быстрый способ, так так укладывается в один проход по памяти, а память нынче — самое медленное место компьютера.
Для маленьких может зависеть от компилятора. Дело в том, что эта проверка будет всегда выдавать положительный ответ, а значит будет надёжно предсказана процессором, и времени займёт или очень мало или вообще отрицательное. Например, в g++, как правило, выкидывание assert-ов приводит к замедлению программы.
КД>То есть, в итоге, вопрос такой — в STL есть что-то подобное transform_range или нужно изобретать свой велосипед?
Даже если вдруг когда и появится (скажем, из буста, упомянутого в другом комменте), всё равно будет работать через тот же push_back. Уровень абстракции может только повышаться, да и сговора с производителями железа никто не отменял.
Re[2]: std::string, копирование с трансформацией символов
Здравствуйте, cures, Вы писали:
КД>>То есть, в итоге, вопрос такой — в STL есть что-то подобное transform_range или нужно изобретать свой велосипед?
C>Даже если вдруг когда и появится (скажем, из буста, упомянутого в другом комменте), всё равно будет работать через тот же push_back. Уровень абстракции может только повышаться, да и сговора с производителями железа никто не отменял.
Вообще говоря, эти transform-итераторы будут передаваться в string::assign, который не факт что работает через push_back.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[3]: std::string, копирование с трансформацией символов
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Вообще говоря, эти transform-итераторы будут передаваться в string::assign, который не факт что работает через push_back.
В хелпе у стринга вижу только один вариант ассигна с итераторами, при этом про них сказано, что должны удовлетворять только требованиям InputIterator, который сугубо последовательный. Оно, конечно, не означает, что разработчики какой-нибудь либы не могут проверить, что там на самом деле рандом акцесс, и оптимизировать этот случай. Но означает (если повар нам не врёт), что не будут обязаны этого делать, а стало быть использование такой оптимизации бедет ненадёжным хаком.
А так-то кто же мешает налабать свои трансформирующие итераторы, скормить ассигну и посмотреть на результат.
Re[2]: std::string, копирование с трансформацией символов
Здравствуйте, cures, Вы писали:
КД>>Не нравится push_back — он будет каждый раз пытаться резервировать место, которое и так уже есть.
C>Не пытаться, а каждый раз проверять. Для больших строк — самый быстрый способ, так так укладывается в один проход по памяти, а память нынче — самое медленное место компьютера. C>Для маленьких может зависеть от компилятора. Дело в том, что эта проверка будет всегда выдавать положительный ответ, а значит будет надёжно предсказана процессором, и времени займёт или очень мало или вообще отрицательное. Например, в g++, как правило, выкидывание assert-ов приводит к замедлению программы.
отрицательное — а как это вообще?
Re[3]: std::string, копирование с трансформацией символов
Здравствуйте, vopl, Вы писали:
V>отрицательное — а как это вообще?
Легко! Внутреннее устройство современных процессоров настолько сложное и закрытое, что компилятор не может толком оценить, какой вариант кода будет дешевле, и, соответственно, оптимизировать порождаемый код для быстродействия.
В современном gcc/g++, а он нынче один из лучших в смысле производительности порождаемого кода, очень часто (на моей практике — почти всегда) добавление внутрь самых горячих циклов assert-а, который всегда выполняется, приводит к ускорению работы программы. В случае push_back имеем то же самое: постоянные проверки, результат которых предсказан, могут ускорить код. Просто за счёт того, что чуть иначе лягут конвейеры инструкций в глубинах процессора.
Такая фигня началась в поздних версиях с мажором 4. У меня была программа, кажется, для задачи 150 с ProjectEuler, которая при компиляции более поздней четвёркой работала ровно вдвое медленней, чем при компиляции более ранней. Ну а когда компилятор тупо сливает двойку самому себе, совершенно не удивительно, что добавление мусора к коду может склонять его к чуть "худшей" оптимизации из предыдущей версии, когда итоговый код работает быстрее.
Re[4]: std::string, копирование с трансформацией символов
Здравствуйте, cures, Вы писали:
V>>отрицательное — а как это вообще?
C>Легко! Внутреннее устройство современных процессоров настолько сложное и закрытое, что компилятор не может толком оценить, какой вариант кода будет дешевле, и, соответственно, оптимизировать порождаемый код для быстродействия. C>В современном gcc/g++, а он нынче один из лучших в смысле производительности порождаемого кода, очень часто (на моей практике — почти всегда) добавление внутрь самых горячих циклов assert-а, который всегда выполняется, приводит к ускорению работы программы. В случае push_back имеем то же самое: постоянные проверки, результат которых предсказан, могут ускорить код. Просто за счёт того, что чуть иначе лягут конвейеры инструкций в глубинах процессора.
аа... понятно о чем речь. Есть серьезные сомнения что производительность увеличивается "почти всегда". Есть мнение что она, наоборот, уменьшается почти всегда, а увеличивается крайне редко, если повезет. К сожалению, без пруфов, говорю просто из опыта.
Re[2]: std::string, копирование с трансформацией символов
Здравствуйте, vadfromnu, Вы писали:
V>Учитывая, что в бусте есть transform_iterator, который делает то, что вам нужно.
Этот подход хорош тем, что позволяет сразу создать объект строки в нужном состояни, возможно, консттантный (в отличие от других решенией, подразумевающих создание модифицируемой заготовки с последующим допиливанием).
Для достижения большей компактности иногда будет полезно использование boost::range с адапторами: