Comments 110
Ну круто же) Спасибо, интересно было почитать историю становления строк.
> CRC32
Как насчет коллизий? Вот допустим у строк A и B коллизия по именам (её шанс мал, но не равен нулю), как не вляпаться? На этапе сборки бандла проверять?
В том диапазоне текста, что набирается при использовании тегов, имен текстур звуков и моделей в игровых движках - я коллизий за все время работы встречал может пару раз. И заканчивалось это изменением имени текстуры, либо переход на 64битный хеш, ибо по времени сравнения u32/u64 разницы нет
А можно ли понять, что случилась коллизия не поймав баг при отладке?
При сборке бандла можно)
а как? там какой-то особый механизм для этого есть? Или сторонняя утилита для проверки?
А зачем хеши? Почему просто на этапе компиляции не собрать все уникальные строки в таблицу и пользоваться указателями? На этапе компиляции, конечно, хеширование имеет смысл, но тут можно и коллизии отловить.
Со ссылками вместо хешей мы, к тому же, получаем доступ к содержимому строки не теряя в скорости сравнения.
Ну так строки с именами ассетов могут быть как часть других ассетов. Например материалы ссылающиеся на текстуры.
Строки в самом движке это 1% использования, в основном строки приходят из ассетов и ссылки на них держать не получится. На этом кадре ассет есть, на следующем уже нет. Так что либо копия, либо ее сурогатт
А как обстоит дело с AnsiString (VCL) и CString (MFC)?
Про игровые строки не знаю, а из обычных QString самая лучшая. Реально много продуманных и полезных методов, не то что в убогой std::string.
Поддерживаю, особенно в последних версиях Qt много полезных методов, делающих всё проще
std::string скорее аскетична..
как и вся std
Главное преимущество QString, на мой взгляд, это хорошая поддержка юникода прямо из коробки. Но зато занимает в два раза больше памяти, чем std::string.
На месте разработчиков Qt я бы добавил еще одну такую же мощную строку для Utf8 (поддержать оба внутренних представления в одной QString наверное сложнее, хотя потенциально тоже возможно). А QByteArray , который иногда используется как байтовая строка, лучше бы переименовать в QBlob.
std::u32string. в С uint32_t и char* symbol, хранить строку можно в rope буфере - дереве, потомучто помимо пространства строки есть еще пространство 2д, строка в 2д будет ограничена краями, и задана размером глифа помимо наполнения самого квадрата глифом, а это значит если сцена реализована через дерево, строки, которые находятся в текстБоксе лучше хранить в дереве тоже
а если это текстура(неизменяемый текст), то это не строка а текстура предпосчитанная из строки движком текста
почему так, потомучто рекурсивный обход дерева при рендере частично выраждается в корутину с нюансами, тоесть рендерим квадратики
template<typename T>
void renderTreePASSUI(const SceneUINode<T>* root, Shader *shader,float dt,glm::mat4 &projection)
{
if (root == nullptr)
return;
// Обработка левого поддерева
renderTreePASSUI(root->left, shader,dt,projection);
// Рендер текущего объекта
const T& obj = root->object;
if (obj.VAO && *obj.VAO)
glBindVertexArray(*obj.VAO);
else{
outputError("Error on RENDER!");
return;
}
if (obj.textureID && *obj.textureID)
glBindTexture(GL_TEXTURE_2D, *obj.textureID);
glm::mat4 model = glm::mat4(1.f);
if (obj.type == OBJUITYPES::STATICUI && obj.pos) {
model = glm::translate(glm::mat4(1.f), *obj.pos);
model = glm::scale(model, obj.ptr->size); // размер 100x100 пикселей
model = glm::mat4(1.0f) * model;
shader->setMat4("uModel", model);
shader->setVec4("color", obj.ptr->color);
glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
// Обработка правого поддерева
renderTreePASSUI(root->right, shader,dt,projection);
}Скрытый текст

рендер квадратиков, как видим векторов тут нету, вектор просто хранит обьекты, лично я считаю это нюансы нелинейного обхода данных, даже при условии что рендерится от начала до конца
тоесть по моему мнению в высоконагруженных приложениях нельзя просто так взять и
for(int i=0; i<size;i++)
renderRectsэто будет грузить почему-то мне кажется
QString прекрасен пока ты живешь внутри экосистемы Qt, но как только тебе нужно вызвать функцию из сторонней библиотеки, которая ожидает const char* или std::string, начинается ад конверсий через toUtf8().constData() и эта "лучшая" строка становится источником постоянной боли)
Это проблемы не Qt, а самого C++ как языка и его библиотеки. С++ далек от совершенства, стандартаня библиотека далека от соврешенства, Qt как может пытается это исправить. Но разумеется, на несовершенном фундаменте совершенное здание не построить:)
Ммм... Как насчёт QString::toStdString()? Ничем не хуже ада конверсий между const char*, std::string, std::string_view.
Не все, что связано с toStdString() может безопасно работать. Использование toStdString() тоже может привнести сюрпризы:
.QString name="Best Language In World";
qDebug() << name;
const char *cname = name.toStdString().c_str();
printf(cname);
.
Во втором выводе будут кракозябры.
Тут весь язык менять надо. Особливо ежели эквивалентное смыкание двух выражений в одно будет работать по-другому:
.
printf( name.toStdString().c_str() );
Немного offtop, но вот так делать нельзя - UB и уязвимость:
printf(cname);Я и говорю: тут весь язык менять надо. Это же ужасно: бесконечные UB и уязвимости на элементарных действиях. Причем все настолько заковыристо, что опасно использовать функцию, предназначенную для целевого действия. И все потому, что проблема возникает вообще еще до вызова функции. Сумасшедший дом.
Вы бы хоть тег /sarcasm добавили, а то я даже не сразу понял тонкость шутки :)
проблема возникает вообще еще до вызова функции
Ну, конкретно здесь проблема возникает непосредственно в момент вызова printf и связана с тем, что cname может содержать символы форматирования %s, %x и т.п.
Хуже всего когда какая-то библиотека навязывает тебе свои строки, в частности это делает Qt, вот че ни делай, а от них не уйдешь ну и код постоянно пронизан конверсиями, потому что другие библиотеки или хотят std::string или свои(что уже большая редкость). Но QString какими бы они классными не были это мракобесие и кошмар любого кода на Qt. Сядьте в комитете договоритесь раз и используйте одни строки. А раньше еще очень любили данные в строки прятать, благо сейчас все чаще вижу std::vector<std::uint8_t>
UDP. Ладно я понимаю что нельзя одни строки придумать для всего с++, есть железки где это неуместно, значит надо какие-то уровни фичей делать в стандарте, минимальный, только с базовыми фичами, и дальше там 1, 2, 3 уровень, да то же не сильно хорошее решение, ну а какой вариант? Либо единый стандарт и с++ работает на калькуляторах, но все страдают, либо абстракции высокого уровня с расплатой в виде перфа и большинство радуются, а кому надо работают с базовыми версиями языка.
UDP2: если Qt когда-то перейдет на std::string я напьюсь в дрова по этому поводу и буду танцевать на столе(рабочем). Но этого не будет.
Конечно не перейдёт, у std::string нет такого функционала. Единственное чего получится добиться – это что вместо QString вам придётся писать QString<std::string> и всё станет в разы медленнее.
О да, боль каждого, кто пытался подружить Qt с условным Boost или любой другой современной C++ библиотекой
Танец с бубном вокруг QString::toStdString() и QString::fromStdString() это обязательная программа
С бубном там танцевать особо не надо, все работает достаточно прозаично, скорее надо следовать четкому соблюдению ритуала. И весь код пронизано вот этой никому не нужной хренью, можно сделать операторы преобразования, но сыкатно, они могут так сработать непредсказуемо и по перфу и по типам повыводить что-то крайне странное. Я в итоге сдался и для половины сделал, как минимум для либы логов и сериализации в json стало проще.
Танец с бубном вокруг QString::toStdString() и QString::fromStdString() это обязательная программа
Это копеешная плата за то, что можно пользоваться нормальными развитыми строками. Они настолько развиты, что дают человеко-читаемые, однозначные и понятные методы преобразования в обе стороны. Сравните пару toStdString/fromStdString вот с этим boost-говнищем:
.
boost::string_ref boost_str_ref = "Hello, Boost!";
std::string std_str(boost_str_ref.data(), boost_str_ref.size());
.
Сущий кошмар.
да то же не сильно хорошее решение, ну а какой вариант?
вариант это вообще не позволять владеющим строкам просачиваться в интерфейс. Если вам внутри не нужно хранить полученную строку, то это просто эффективнее. Если нужно хранить, то принимая владеющий тип в функцию вы раскрываете деталь реализации.
std::string_view хороший вариант для стабильного апи. Он может работать почти с любой строкой (разве что есть строки которые не аллоцированы как один кусок памяти).
Ещё более спартанский вариант это char* + size_t. Это для сишного апи.
Но на деле это мало кто будет о таких мелочах задумываться. Влепят аппрув и поехали.. Вот и живем так что весь код пропитан QString::toStdWStribg / fromStdWString
О это вы конечно загнули, тут благо в целом народ понял совсем недавно чем ссылка от объекта отличается и если в функцию приходит const std::string&, то не надо это прихранивать у себя без явного копирования и вообще относиться к этой вещи как "не твое", а вы уже string_view хотите, кому-то придет в голову их прихранивать у себя, ну а че вот же я копию у себя сделал и счастливый. Опять придется ждать пока коллективное бессознательное обучится новой магии вне Хогвартса.
Он может работать почти с любой строкой (разве что есть строки которые не аллоцированы как один кусок памяти).
А как std::string_view будет у вас работать с QString, если у последнего внутри QChar и он внутри UTF-16 содержит? sizeof(QChar) == 2
А как std::string_view будет у вас работать с QString, если у последнего внутри QChar и он внутри UTF-16 содержит? sizeof(QChar) == 2
Работает с разными типами строк - std::string, CString, MyCoolString.
С другим типом char_type конечно не будет.
Это проблемка, но я для себя вижу как решение просто использовать везде utf8 строки с char
как решение просто использовать везде utf8 строки с
char
А как вы это планируете использовать конкретно с Qt? Я не спорю, string_view это очень хорошая концепция как и другие view, но разговор у нас начался с конкретной моей боли при использование Qt, а именно постоянной конвертации строк туда сюда обратно.
да ни как) как я и написал в первом ответе:
Вот и живем так что весь код пропитан QString::toStdWStribg / fromStdWString
Так что это и моя боль :(
Изначально я отвечал на UPD, где вы предлагали способы решения этого зоопарка. Поэтому я не имел ввиду что мои способом можно решить проблема с Qt. Уж извините если ввел в заблуждение :)
Но.. мне пришла в голову безумная идея - использовать type erasure чтобы можно было положить разные типы символов в один "string_view". Как пример такого контейнера это std::ranges::any_view
не получится, потому что любая попытка стереть тип у строк упирается в несовместимость моделей представления данных, а не в отсутствие абстракции. Если возвращать условный char32_t то придется декодировать UTF-8/UTF-16/UTF-32, которые тоже разные. Получается что type erasure превращается в полноценный transcoding layer и очередной тринадцатый станадарт и несет огромные накладные расходы, значит это уже не совсем укладывается стринг вьюху, т.е. пока это не будет сделано на уровне языка как единое решение, так или иначе разные библиотеку будут велосипедить своЁ
Поэтому я не имел ввиду что мои способом можно решить проблема с Qt. Уж извините если ввел в заблуждение :)
Понял, да, ввелся немного в заблуждение, подумал, может есть какой-то секрет. Надежда была )))
Меня больше напрягает того что никто не может определится что такое строка или символ впринципе. В итоге если хочешь обойти строку то должен учитывать что utf-8 строка будет тупо байты сыпать, и ты ещё должен их сборать и понять какой символ перед тобой. Дурка
В итоге строка это тупо динамический массив, по сути (но со специфическим char типом), чем иногда пользуются различные библиотеки (часто такое в хэшировании видел, что они байты хэша в std::string хранят, а не в vector каком-нибудь)
В итоге строка это тупо динамический массив, по сути (но со специфическим char типом), чем иногда пользуются различные библиотеки (часто такое в хэшировании видел, что они байты хэша в std::string хранят, а не в vector каком-нибудь)
так раньше делали, потому что в vector какого типа? целочисленные типы не имели фиксированного размера, имел только char, а значит надо делать vector<char>, а зачем его делать если string он и есть. Сейчас все чаще и прикладные программисты и разработчики библиотек начинают использовать std::vector<std::uint8_t> или может быть даже std::vector<std::byte>
Не раскрыта тема TString в Яндексе.
А это что за зверь то такой?
В Яндексе есть Аркадия -- монорепа, которая в том числе содержит стандартную библиотеку Яндекса, которую использовал сначала Поиск, а потом и всё остальные проекты. Эта библиотека содержала тип Stroka (напомню, что Яндекс.Поиск появился раньше, чем std::string), который был был позже переименован в TString. В году 2019 TString хотели заменить на std::string, но чем всё кончилось, я не знаю.
Кстати, был тип Wtroka (аналог std::wstring), который произносился как "штрока".
Да многие большие проекты своими строками обзаводятся. В последнее время с llvm::StringRef часто встречаюсь, ещё из открытого навскидку blink::String, в одном из закрытых проектов долго жили свои строки со стандартным интерфейсом, но в какой то момент просто сказали using our_string = std::string (или wstring, в зависимости от платформы).
Класс llvm::StringRef никак не аналог std::string. Это скорее аналог std::string_view так как он не владеет данными, а содержит только ссылку на них
Согласен, но в коде llvm чаще используется именно StringRef чтобы не перевыделять/копировать память.
Не понял, почему "но". Ведь данные в AST статичные и перевыделять память по новой действительно не нужно.
Я к тому, что если по смыслу в функции нужно что-то сделать строковыми данными - чаще всего она принимает StringRef и пользуется его интерфейсом. Да, он не занимается вопросом аллокации памяти - но это только часть функционала строки.
Ведь данные в AST
llvm - это далеко не только компилятор ;) Но и при банальном парсинге входных текстовых файлов в коде обычно StringRef.
AST, это только пример статичной структуры, узлы которой не требуют перевыделения памяти.
И структура банального парсинга входных текстовых файлов будет тоже статичной, для анализа узлов в которой не требуется перевыделять память (после чтения файла данные уже находятся в ней), поэтому использовать StringRef или std::string_view совершенно логично.
Круто, даже не задумывался, что зоопарк такой большой.
Когда начинал программировать и узнал, что литералы живут до конца программы - хотелось их не использовать вовсе, казалось, что столько памяти пропадает зря.
Самое забавное, что половину этого зверинца порождает не столько C++, сколько разные требования по времени жизни и владению памятью
Читать просто невозможно. ИИ нагенерировал главу для учебника по биологии с каким-то бесконечным количеством грубейших ошибок. В области C и C++, разумеется.
Всё как в дикой природе - волк с собой счётчик овец не носит.
Какой волк, какой счетчик овец? Это техническая статья или постмодернистские галлюцинации?
Главная магия фиксированных массивов в том что они живут на стеке.
Во-первых, нет никакой магии.
Во-вторых, фиксированные массивы не живут на стеке. Они вообще не живут. Их расположение определяется местом, где они определены и это далеко не всегда стек.
вы можете сами это посмотреть в исходниках, которые все еще доступны на гитхабе
Типичный LLM ход.
Весь этот бред разбирать не вижу никакого смысла. Это просто мусор.
Какой волк, какой счетчик овец? Это техническая статья или постмодернистские галлюцинации?
Счётчик количества символов в строке. Простейшая аллегория же.
А это смотря в каком настроении и с какой целью читать статью. Я вот базами занимаюсь. Базово знаком с std::string, зачем вот мне оно надо - глубже в эту вашу паучью банку лезть? Если бы не забавные аналогии, вряд ли бы осилил хотя бы до середины. А так прочитал всю в общеобразовательных целях.
Среди всех прочитанных сегодня мной статей это самая неллм статья. Впрочем, всё субъективно.
у автора хорошие статьи, и эта статья хорошая - со смачным удовольствием влепил плюс. отнюдь не мусор, не ваодите всех в заблуждение
Читаю и прямо флешбеки из начала 2000 ловлю
Войны между адептами MFC CString, борландовского AnsiString и std::string...
А потом приходил Qt со своим QString и говорил "подержите мое пиво"
Каждая библиотека, каждый фреймворк считал своим долгом родить собственную реализацию строки, и каждая была несовместима с соседней. Ад конверсий toStdString().c_str() закалил не одно поколение плюсовиков
Нелепые метафоры - один из главных признаков нейротворчества
Отдельное спасибо моему другу Саше Васильеву за предоставленные рисунки и художественную обработку.
Это ж ИИ обработка, не?
Это подбор фоток под темы абзацев + несколько фильтров в фотошопе + ручные правки. Я пробовал нагенерить это промтами, но в итоге выбрал лимиты и сдался. Оказалось проще отдать человеку :)
1/3 статьи читается с интересом, затем - перебор с объёмом текста и сложно держать внимание на концепциях.
Вывод, характеризующий "ИТ": в 2025-м году от Х.Р. хомяки не могут остановиться - улучшают HTML и борются со строками.
И прогноз на будущее:
Бэк ту гуд.
Отрадно наблюдать, как в 2025-м году в пабликах "Ассемблер" (по-русски) идёт горячее обсуждение программирования под ZX Spectrum ~1988г. Полагаю, после периода стихийно-массовго программизма в Руси придётся начинать с этого чекпоинта. :)
Тут более техничная статья про xstring https://habr.com/ru/articles/873016/ и поменьше объемом
bool compare(const std::string& s1, const std::string& s2) {
return s1 == s2;
}
std::string str = "hello";
compare(str, "world"); // Создаёт временный std::string, аллокацияА почему при передаче по константной ссылке создаётся новая версия строки s1?
А дело не в s1
а зачем вообще - я реально не понимаю проблему (мотивировочную часть).
Есть ссылки. Они куда-то передаются - собственно это должно работать с zero-copy.
rhs всегда объявляется const (т.е. даже слишком много реализаций оверлодить не надо).
Проблема с чем? С литералом "world"?
Ну да только в С++20 додумалисть сделать ==( const std::string &, const char *).
поидее тогда если так то
bool cmp11(const std::string_view& s1, const std::string_view& s2) {
return s1 == s2;
}
int main() {
std::string s1("hello");//явное владение
std::string s2("world");//явное владение
s1.clear();s2.clear();
std::cout<<cmp11(s1,s2)<<std::endl;
//отправка указателей на владельцев поидее
return 0;
}точно так же и с выводом можно сделать поидее, явно аллоцировать владение строки(но не стараться хранить сразу стринг_вью там даже если покажется, что работает в кадрах будет видно, что где-то будет мусор), чтобы оно жило в своём кадре и отправлять только интерфейс на строку, но при условии, что владение существует
С литералом world, но ещё до сравнения. Функция принимает в качестве аргумента только ссылку на std::string - соответственно для передачи в функцию из этого литерала будет сконструирован экземпляр std::string, что и вызовет аллокацию и копирование строки.
Если принимать аргументом не std::string, а std::string_view, то все станет гораздо лучше и универсальнее.
Ну да.
Достаточно же не в 2020 году, а в 2003 / 2011 году написать:
bool operator==( const std::string & lhs, const char * rhs);И не пришлось бы переходить на новый тип данных.
Но видимо комитет по стандарту лёгких путей не ищет.
а как же Юникод, это же std::u32string и хэш uint32_t в char* с размером глифа, который отразится в аабб, разве это зоопарк или мы обсуждаем только ASCII? в аски просто стринг, или char строка и инт - кодпойнт, с аабб, по обратной связи вы возможно спросите как управлять такой строкой и текстом.
так ответ и кроется в реализации, если мы рисуем строки и они не изменны никогда, это заведомо текстура, если рисуем текстовый редактор, то пользователь манипулирует строкой через механизм 2д как бы мы этого не хотели, границы окна, границы текстБокса, границы глифа, так это еще учитывая то что у вас никогда не будет строки прямой, там будет сущность буффера даже в С++, изза того, что проще писать хэш во время открытия файла, соотв если это не текстовый редактор, надо реализовать механизм ввода символов через Юникод же, а это текстуры глифов с позициями, и там еще будут нюансы с линейностью, тоесть всё таки это через вектор да, ну тоесть vector-string-size? нет конечно, мы получаем метаданные глифа до отрисовки, в нужной локали, и реализовываем буффер вывода строки
если это 8битные вещи, там уже есть преднастроенные глифы, битмапы же тоже
странно тут как не крути буффер - его реализация будет одна, и она не будет string напрямую, это будут кусочки памяти char* от кодпойнта же
Это было проще для железа: не нужно хранить лишнюю длину, просто иди по памяти, пока не встретишь ноль.
Т.е. терминирующий '/0' место не занимает, ага.
Тупое решение было, а не для экономии памяти. И обернулось оно множеством проблем в будущем.
Предпреждая комментарии, что это один байт -- на PDP-11, где язык С был применён массово, минимально в памяти адресовалось не меньше слова (16 бит, отсюда и размер char -- по умолчанию 16 бит в большинстве случаев).
Т.о. никаким экономичным способом хранения строки тут и не пахло.
Это было проще для железа: не нужно хранить лишнюю длину, просто иди по памяти, пока не встретишь ноль.
Это наследие PDP-11 (возможно всей архитектуры PDP).
- Там была аппаратная работа с 0-terminated строками.
- Ну её и внесли в стандарт С (локально казалось хорошим решением).
- Оттуда в С++ (локально казалось хорошим решением).
Это экономно с точки зрения хранения самого указателя. Указатель занимает в памяти/на стеке один размер указателя (а не два), передаётся как аргумент в одном регистре (а не в двух), и так далее.
Аплодирую стоя. Во толково описал и для самых маленьких…
Прочитав статью я сделал такой вывод:
«Странная картина вырисовывается: Комитет просто не может родить стройную систему, потому что вечно не успевает за хотелками железа и маркетинга. Бизнесу надо портировать на XBOX "вчера"? Окей, берем костыль, а не пишем архитектуру. Никто не будет ждать "правильного" стандарта. Вот и получается, что мы вынуждены скрещивать носорогов с бегемотами, просто чтобы продакшн не встал. Ощущение, что язык формируют не инженеры, а дедлайны».
Прогнал мой вердикт через ллм, и она меня поправила:
«Вообще интересная картина вырисовывается.
Кажется, что Комитет тут даже не при чем — он просто не может создать "серебряную пулю". Статья отлично показывает, что единую стройную систему построить невозможно физически: бизнесу нужно "вчера и на XBOX", эмбеддерам нужно без аллокаций на стеке, а геймдеву — чтобы парсилось за 0 тактов.
Получается, этот зоопарк — неизбежная цена за то, что C++ пытается усидеть на всех стульях сразу. Мы скрещиваем носорогов с бегемотами не потому, что не умеем кодить, а потому что универсальный инструмент проиграет специализированному в каждой конкретной нише.
Хотя впечатление бардака, конечно, не отпускает».
Все так, единственная поправочка, бизнес уже продал игру с костылем владельцу xbox, выплатил зп разрабам и рекламирует новую игру, где здесь место и время для поиска идеальной строки в степях дедлайнов решительно непонятно. А если серьезно, то есть устоявшийся набор решений eastl::fixed_string, fstring, xstring и попытки заиспользовать чтото другое быстро отсекутся на ревью думающим лидом. Бардак конечно, но бардак управляемый...
Да я кстати не делал акцент на дедлайнах, скорее на «реактивности» — все здесь и сейчас, баги исправлять потом будем.
Вот короче оригинал-оригинал:
вот статья на хабре: https://habr.com/ru/articles/968536/
нормально будет так ответить:
»Вообще странная конечно картина по выводам нарисовывается:
Комитет не может организовать стройную систему из-за быстро измененяющегося рынка железа и бизнеса с маркетингом, которые вечно пытаются сэкономить денег. Надо портировать срочно на XBOX? Окей, зачем нанимать новую команду, писать весь стек с нуля? — Не-а, в диком бизнесе так не работает. Нам нужно всё здесь и сейчас. Вот и начинают скрещивать носорогов с бегемотами по сути.
Не знаю. У меня лично такое впечатление сложилось.»?
Так это ровно как и должно быть для не-киллер фичи языка.
Если у вас есть несколько значимых критериев - обычно на практике означает что у вас есть целый набор парето-оптимальных состояний.
И для того, чтобы выбрать "лучшее" из этого набора парето-оптимальных состояний вам надо знать какой из критериев важнее. Обычно для этого надо уметь угадывать будущее, что сложно.
гипотетический пример:
virtue(std::string) = { 5/* идеоматичность */, 1/*производительность*/};
virtue(std::string_view) = { 2/* идеоматичность */, 4/*производительность*/}Заметьте - вы не можете сказать "что лучше" {5, 1} или {2, 4} пока вам не скажут что для вас важнее "идеоматичность" или "производительность".
А когда стало известно, какой критерий важнее, - выясняется что правильный тип данных нужен вчера!
Огонь статья. Сколько же вы придумали животных для нее?
Что? Строка со счётчиком ссылок в стандартном C++? Знакомьтесь: std::runtime_error.
Может стоит все-таки признать, что содержание и корректность важнее, чем картинка с дикобразом и аллегории из серии "кролики скачут друг на дружке в норе и месят глину"?
Я не эксперт по биологии, но в C++ разбираюсь чуть лучше автора сего графоманского опуса. Это типичный материал в духе "стремительным домкратом", написанный человеком, который "плавает", что в юникоде, что в языке C++, что в игровых движках, что в истории вычислительной техники.
Перечислю только некоторые грубые ошибки и неточности, которые сразу бросились в глаза.
Главная магия фиксированных массивов в том что они живут на стеке. Объявление char buf[N] резервирует байты прямо там и гарантирует что черепаха всегда дома.
Наглая ложь. Кроме стека, массив в Си может быть глобальным или находиться в куче.
Если поле типа char buf[N] объявлено полем в структуре, то вообще невозможно сказать, где этот массив будет находиться.
Литералы (...) помещаются в секцию .rodata, которую операционная система помечает как read only после загрузки программы. Попытка записать туда что-то вызывает немедленный segfault
Секция .rodata это секция в файлах формата ELF, который не имеет вообще никакого отношения к языку C++ и его стандарту. Равно как и к стандарту языка Си.
А еще ELF файл может быть загружен операционной системой, которая не поддерживает защиту памяти (например, из-за аппаратных ограничений). Поэтому немедленного segfault может и не быть.
std::string (C++03/11)
Нет никакого C++03/11. C++03 это абсолютно минорный апдейт стандарта C++98.
Стандарт C++11 это революция в языке, "совершенно новый язык".
Поэтому C++03 и C++11 в одну кучу будет смешивать только тот, кто вообще ничего не знает про эти стандарты.
И так же не знает, что std::string появился в C++98.
В природе в принципе не существует стандарта C++ в котором нет этого типа, поэтому приписки типа "C++03/11" это бессмыслица.
Добавили RAII, методы, автоматическое управление памятью
Это просто набор случайных слов.
Добавили классы с конструкторами, деструкторами и методами. Автоматические управление ресурсами (в том числе памятью) через вызов конструктора и деструктора стали называть RAII.
хватает классических проблем с (...) весёлой и порой непредсказуемой работой SSO
А можно поподробнее? Где там веселье и непредсказуемость? По слухам какая-то магия? Кролики съели кошачий корм, а обиделись на них за это рыбки?
Каждый раз когда строку передавали в функцию через const std::string& существовал риск, что внутри функции кто нибудь хитрый создаст новую строку из литерала для сравнения
В примере, который это иллюстрирует, никто новую строку ВНУТРИ функции не создает. Строка создается снаружи. Просто для того, чтобы соответствовать аргументам функции compare().
И я в упор не вижу описанного риска, ибо сравнение внутри функции будет выглядеть как s == "abc".
Понимания мотивации создания string_view у автора нет.
Просто посмотреть это теперь вообще философия C++17 - меньше копирования, больше умных view типов и больше производительности через zero cost абстракции и мелкие хаки.
Я хорошо знаю стандарты и "просто посмотреть" это не философия C++17. Это просто кто-то разогнался на собачках, кошечках, кроликах и незаметно для себя дошел до оценки философии стандарта языка, который толком и не знает.
На string_view теперь пишут высокопроизводительный код хитро организуя время жизни полноценных строк.
Точно такой же высокопроизводительный код писали и будут писать на основе std::string.
И нет тут никакой хитрости. Если ошибешься с временем жизни, то получишь "немедленный segfault".
В 1998 году вышел C++98 со своим std::string
Таааак! Так вон же выше написано, что тип появился в C++03/11! "К середине 2000-х годов std::string стал стандартом".
Попытки подружить QString со std::string приводили к созданию временного QByteArray через toUtf8
Конвертация QString в string опирается на локаль операционной системы, которая, мягко говоря, далеко не всегда UTF-8.
И вообще, зачем впихивать невпихуемое и юникод строку утрамбовывать в тип, который не предназначен для хранения такого рода данных?
Конструкции вроде str.toLower или str.split вообще издалека можно спутать с Python
Та ладно! Их можно спутать с любым языком, где есть класс строка с методами. А именно Java, C#, JavaScript, Swift, Kotlin, Go, Scala... Что ж мы так рано остановились?
Вся магия как обычно в Qt спрятана глубоко в PImpl и внутри их мета системы, которая по слухам умеет всё.
Большинство классов в Qt построено на PImpl. И что? К чему это?
И мета система к вещам типа QString::split() вообще не имеет никакого отношения. Такая вот магия. По слухам.
Windows пошла своим путём и выбрала 16 бит, потому что их API базировался на UTF-16, но это были свои, уникальные 16 бит.
Ничего уникального не было. Это был и есть стандарт UTF-16 LE.
а MacOS вообще начала отходить от wchar_t в сторону собственной реализации
MacOS никуда не отходила. В качестве юникода был выбран стандартный UTF-16.
В компиляторах на платформе wchar_t был UTF-32.
Плюсы на маках всегда были гражданами второго сорта, поэтому Apple тут просто спустила все на тормозах, вместо того, чтобы привести в соответствие.
Попытки конвертировать между std::string и std::wstring на Windows превращались в многочасовое путешествие по документации
Как я уже писал выше, потому что это неоднозначное и очень опасное преобразование само по себе. Как значение double сохранить в char. Ну или как пенис слона засунуть в вагину кролика, если использовать более близкие к статье аналогии.
И даже в эпоху, когда не было LLM, даже программисты, никогда в жизни не видевшие Win32 API, в течении минуты находили MultiByteToWideChar и WideCharToMultiByte.
до появления std::filesystem в C++17 единственным способом открыть файл с нелатинским именем на Windows было конвертировать путь в std::wstring и использовать нестандартные расширения компилятора
Ложь. До появления "просто посмотреть" стандарта (в котором, наверное, и файлы открываются исключительно в режиме "только чтение", чтобы только посмотреть) существовал тысяча и один способ открыть файл с юникод именем под Windows.
Например, используя функцию CreateFileW().
Или через boost filesystem.
Или через QFile.
Или...
Если вы посмотрите на malloc в стандартном std::string (...)
То вы его там будите очень долго искать. И не найдете.
Потому что по стандарту std::string обязан использовать new char[].
Вся архитектура, весь networking, весь рендеринг были заточены под коридоры и арены жанра. И строки в движке были утилитарными инструментами для этой цели - имена уровней, названия оружия, debug сообщения, network packets.
Строки это строки. Они никак не были "заточены" под коридоры и название оружия. Ходили слухи, что там была какая-то магия и их даже можно было использовать для имен собачек, кошечек и утконосов.
МакКарти внес фундаментальную идею о неизменяемых объектах-идентификаторах: в программах идентификаторы сравниваются тысячи раз но создаются редко. Имена переменных, функций, классов - всё это известно на этапе написания кода и почти не меняется в рантайме, тогда зачем сравнивать их посимвольно каждый раз?
Да, да, классы в LISP 60-х годов. У которых почти иногда никогда редко не меняется имя в рантайме.
Или это мы в C++ программе постоянно посимвольно сравниваем имен классов и переменных? И почти иногда всегда меняем в рантайме... Магия!
до появления std::filesystem в C++17 единственным способом открыть файл с нелатинским именем на Windows
И это я еще на написал, про возможность в лоб использовать имя, закодированное в текущей локали.
Хабр - открытая площадка, где каждый может написать материал в том стиле, который предпочитает нужным. Надеюсь вам не составит большого труда сделать статью об этом, бо время для комментария с подробным анализом нашлось же... Тегните меня там - покритикую ваш опус в духе стремительного домкрата
Конвертация QString в string опирается на локаль операционной системы, которая, мягко говоря, далеко не всегда UTF-8.
Так было до Qt5. Теперь toStdString() всегда приводит к toUtf8() под капотом.

Я гналась за вами три дня чтобы сказать как вы мне безразличны
Весь этот бред разбирать не вижу никакого смысла. Это просто мусор. (c) cdriper
А вы последовательны )
Позвольте, но откуда столько гнева? Замечательно видеть человека глубоко в теме и прочитать столь развернутый комментарий, но все то же самое можно было оформить в более мягком ключе, указывающем на недостатки статьи.
Очень печалит культ токсичности в отечественном сегменте, не понимаю откуда и почему так повелось, это потенциально может вредить обмену опытом.
потому что это статья уровня "стремительным домкратом"
на техническом ресурсы пипл с удовольствием хавает дилетанские статьи и даже создает культ вокруг их авторов
Ну всегда есть вариант написать - свою с дамами и преферансом, ктож запрещает. Если хочется технических, то их тоже есть (https://habr.com/ru/articles/687146/), но как показывает практика написания технических статей они редко собирают больше 10 плюсов
А ещё можно вспомнить про то, что в Python'е есть:
просто строки,
r-строки (raw),
f-строки.
И, при этом, это всё ещё и неизменяемое.
А, ведь, ещё всякие там Java и C# со StringBuilder'ами...
Скажите пожалуйста, чьего авторства эти замечательные иллюстрации?


Зоопарк строк в вашем C++ коде?