Ценность квалифицированного программиста смещается в сторону умения проводить обзоры кода. Генерировать код становится проще, но всё так же важно проверять его с точки зрения качества декомпозиции, корректности реализации, эффективности, безопасности. Посмотрим на примере маленького проекта markus, созданного с помощью Claude Opus, почему важно понимать сгенерированный код и уметь видеть, что скрывает красивый текст программы.
Проект markus
Я искал какой-то проект, сгенерированный с помощью ИИ, и мне попалсяmarkus.
Это single-header библиотека для парсинга текста и конвертации его в HTML. Создана с помощью Claude Opus 4.5.
Мне понравилось, что библиотека крошечная. Можно её просмотреть и даже погрузиться в изучение отдельных фрагментов кода. Если перед вами сразу много вайб-кода, то как-то даже не хочется начинать его смотреть.
Библиотека состоит из одного файлаmarkus.h, содержащего 6484 строки на C++20. По существу кода ещё меньше. Некоторые алгоритмы в целях оптимизации используют табличные методы. Четыре таблицы представлены в коде бессмысленно вытянуто.Пример:
Если таблицы не считать, то, собственно, кода ещё на 1000 строк меньше. Анализатор PVS-Studio ничего особенного в проекте не нашёл, но проверка стала поводом посмотреть некоторые фрагменты поподробнее. И вот уже специалист, то есть я, могу выделить моменты, которые стоит обсудить.
Проблематика вайб-кодинга
Генерация кода с помощью ИИ может очаровать, особенно неискушённого программиста, который не работал с большими старыми проектами.
Кто сталкивался, тот понимает, что главная задача — не написать код, а сделать так, чтобы его можно было сопровождать и развивать. Об этом хорошонаписалРоберт Мартин:
Без присмотра AI будет нагромождать код поверх кода. У него нет чувства большой картины. Архитектура, вероятно, за пределами его возможностей.
Так что изучение принципов декомпозиции, умение правильной постановки задач и т. д. становятся только более актуальной компетенцией программиста.
Но я хочу посвятить статью другой особенности вайб-кодинга, про которую мало говорят: это очарование процессом генерации кода и, как следствие,излишнее доверие к нему. Редкое исключение — заметка “ИИ пишет код быстрее. Но не понимает, что пишет”.
Убедившись, что простые задачи решаются хорошо или даже лучше, чем это бы сделали вы сами, теряется критичность взгляда. Кажется, что и в больших задачах качество будет высоким, код оптимизированным и т. д.
За примером далеко ходить не надо. Я ради интереса просил ИИ написать код для быстрого подсчёта количества единичных бит в больших массивах. Я был очарован предложенными алгоритмами. Про многие интересные решения я даже не знал, хотя в своё время прочитал в книге разбор целого ряда алгоритмов на эту тему.
Смотришь на предложенный код подсчёта бит и думаешь: “А не пора ли податься в фермеры?” Сам бы я такой код не написал и не догадался бы его поискать, т. к. просто не знаю про такие алгоритмы.
Это восхищение проецируется на весь код, созданный ИИ. Когда я только заглянул в код markus, то подумал, что анализировать его смысла нет: всё красиво, оптимизированные алгоритмы и т. д:
- Тонкая настройка оптимизации компилятора с помощью подсказок наиболее вероятных веток исполнения [[unlikely]];
- Использование условной компиляции на основеif constexpr;
- Использование оптимизированных табличных алгоритмов;
- Генерация SIMD-Friendly Helper Functions;
- Использование контейнеров из пространства имёнstd::pmrдля задания собственной стратегии выделения памяти.
Код солиден, что там смотреть? Оказывается, есть что. Ощущение, что код оптимизирован, — ложное. Вернее, местами он действительно оптимизирован, а местами всё плохо.
К чести автора проекта хочу сказать, что он молодец и взвешенно подходит к описанию проекта. Райан явно предупреждает:
DO NOT USE IN PRODUCTION. This is completely vibe coded and has not undergone any reviews for memory safety. Its performance is also 2-3x slower than cmark.
Какую проблему я хочу подсветить статьёй? Код может выглядит педантичным, оптимизированным и безопасным, но таковым не является.
Хорошо, что работу проекта markus можно сравнить с другим решением и понять, что он получился не очень быстрым. А если сравнивать не с чем? Нужно экспертное мнение человека. Беда в том, что многим кажется, что теперь эксперт и обзор кода не нужны.
Нужен ли эксперт?
А может быть, оценка кода экспертом действительно не нужна?
Вы создали код. Он выглядит красиво и опрятно. Он выглядит оптимизированным и работает. Может, этого достаточно?
На мой взгляд, без оценки можно обойтись, если выполняются все четыре условия:
- Никто не пострадает и не понесёт убытки, если код работает не так, как задумывалось, из-за багов или неправильной интерпретации ТЗ.
- Код безответственный в плане безопасности. Никто не будет его ломать, так как это никому не нужно.
- Скорость работы кода неважна.
- Не предполагается, что у проекта будет долгий жизненный цикл. Его не потребуется развивать десятилетиями, сопровождать, адаптировать для других платформ и т. д.
Во всех остальных случаях, извините, все требования к проверке качества кода в силе и ещё более актуальны. К вайб-коду должны применяться все те же процессы, что описываются в SSDLC / стандартеРБПО.
Почему я строг к ИИ
Я не строг, а требователен. Для реализации проекта взят язык С++.Суть использования C++ — построение быстрых, эффективных приложений, экономно использующих память. Если вам не нужна быстрая программа, не надо использовать C++.
Раз уж выбран C++ для создания быстрого приложения, то задача именно написать быстрый код. Неважно, пишет его человек или это делает ИИ. Если быстрый код не получился — задача не решена.
Мой личный интерес — изучать, что такое вайб-код и как статические анализаторы могут помочь контролировать его надёжность. Вайб-коду, как и людям, свойственно антипаттерны, но другие. Есть смысл развивать анализатор PVS-Studio с учётом этих особенностей.
Попутно, раз я всё равно смотрю проекты, есть повод написать статью. У меня нет задачи ругать вайб-код и в целом вайб-кодинг. Это глупо, это просто инструмент. Но в отличие от евангелистов ИИ у меня нет задачи продвигать эти технологии, поэтому если я вижу, что код — хрень, я так и напишу.
SIMD-friendly код
Комментарий к функцииIsSpanBlankобещает, что код составлен из расчёта облегчить компилятору задачу его векторизации, например, с помощью SSE-инструкций.
Суть оптимизации — несколько элементов обрабатываются одновременно, чтобы определить, хранят ли они пробельные символы.
Код создаёт впечатление, что ИИ знает, что делает. Внутренний цикл всегда обрабатывает по 8 символов, и предполагается, что эта часть может быть оптимизирована с помощьSIMD-инструкций. Оставшийся хвост символов обрабатывается отдельным простым циклом.
Мне этот код показался подозрительным тем, что на самом деле байты обрабатываются всё так же последовательно. Вложенный цикл не несёт чего-то полезного. На мой взгляд, этот код не лучше и не хуже простейшего варианта его реализации.
Я бы написал именно такой код. Если бы я понимал, что это узкое место и нужно непременно сделать код как можно быстрее, я бы пошёл искать решение, построенное на интринсиках.
Какой-то другой вариант, например, с вложенным циклом, я бы делать не решился. Я хоть что-то и понимаю в оптимизации, но не настолько, чтобы отважиться писать хитрый код с целью подсказать компилятору.
Можно сказать: “Вот! Ты слаб! А Claude Opus взял и сделал. Взял и написал более умный вариант”.
Давайте попробуем сравнить скорость. Мойколлега Филипппровёл небольшой эксперимент сGCCиClang:
- Формируем одинаковый набор данных для обоих бенчмарков: генерируем 1 000 000 строк случайной длины от 1 до 127 символов с произвольным содержимым. Генерация наборов не учитывается в замерах.
- Подаём на вход алгоритму набор данных и для каждой строки вычисляем, содержит ли она пробельные символы. Замеряем общее время.
В обоих вариантах код компилируется с агрессивными оптимизациями-O3. Можно заметить, что для обоих компиляторов простой вариант функции лучше, чем с вложенным циклом.
Быть может, просто надо какой-то волшебный ключ оптимизации указать, чтобы вайб-код оптимизировался лучше? Нет такого ключа. А если будет, компилятор и без подсказки сам сделает столь же эффективный вариант из простого кода с одним циклом.
Параллельно с коллегой тоже поэкспериментировал с двумя рассмотренными и тремя новыми вариантами алгоритма. Я использовал компилятор MSVC. Давайте посмотрим, какие результаты замеров получились.
Во-первых, мне подсказали, что ситуацию можно улучшить, заменив в изначальном варианте||на|. Short-circuit evaluation в нашем случае не помогает, а только вредит. Улучшенная проверка:
Во-вторых, я спросил DeepSeek. Он предложил вариант оптимизации, построенный на битовых операциях:
И напоследок я попросил у DeepSeek оптимизированную функцию, по-настоящему использующую векторизацию. Вот что получилось:
Теперь результаты. Хотя 32-битные сборки сейчас неактуальны, ради интереса я и её запустил. Замеры выполнялись на массиве размером 212 мегабайт, заполненном случайными пробельными символами. Выполнение потока привязывается к одному предварительно прогретому ядру. Если непонятно, о чём идёт речь, загляните в статью “Вечный вопрос измерения времени”.
Хорошо заметно, что 64-битный код получился намного эффективнее, кроме случая, где в ход идёт SSE2, но и там в любом случае всё быстро.
Код, сгенерированный Claude Opus для проекта markus, худший из всех вариантов.
Даже самый простой вариант с обыкновенным циклом не только быстрее, но ещё и короче. Дополнительные строки кода пошли только во вред.
В лидерах код на интристиках, но платой тому — ограничение переносимости.
Кто-то скажет, что я зря переживаю. Да, Claude Opus выбрал неудачно решение, но ведь DeepSeek предложил вполне хорошее, быстрое и переносимое решение.
Согласен. Вот только в проекте мы видим неудачный код. Код выглядит оптимизированным, но таковым не является. Задача не решена. Нужен эксперт, который это поймёт и выберет другое решение.
В будущем проблема будет нарастать. Сгенерированного кода будет всё больше, а людей, которые могут делать обзор кода, останется столько же. Затем системы начнут учиться на таком же “оптимизированном” коде... Ох, интересные времена нас ждут.
Сравнение строк: цветочки
Моё внимание привлекло предупреждение PVS-Studio:V590[CWE-571] Consider inspecting this expression. The expression is excessive or contains a misprint. markus.h 5987
Действительно, код содержит избыточную проверку:
Тут, на самом деле, ничего страшного. Ну лишняя проверка, которую и так выкинет компилятор. Для красоты код, конечно, стоит поправить, но не более того:
Однако глаза скользнули на код нижеif. И вот там избыточность, которая достойна вашего внимания:
Здесь выполняется проверка, что строка начинается с"
Так себе решение с точки зрения эффективности. Создание временной строки — это тяжёлая операция, несмотря на то, что используетсяstd::pmr::string. Всё равно ведь надо символы копировать в память, да ещё по одному. Кстати, здесь простойstd::stringбудет не хуже, так как строка не более 15 символов и сработаетSmall String Optimization.
Лучше написать функцию, выполняющую сравнение без учёта регистра. Например, такую:
Это просто пример. Можно реализовать алгоритм по-другому или сделатьistring_view, написав своиchar_traits, и так далее. Главное — мы избавляемся от создания временной строки и упрощаем код, выполняющий проверку:
Ещё ярче необходимость создания функции сравнения станет видна в следующей главе. Как говорится, это только цветочки, а ягодки будут впереди.
Сравнение строк: ягодки
Просматривая другие предупреждения PVS-Studio, я обратил внимание на этот вектор строк:
Анализатору он не нравится:V1096The ‘type1_tags’ variable with static storage duration is declared inside the inline function with external linkage. This may lead to ODR violation. markus.h 4781
Нет смысла рассматривать это предупреждение в статье. Куда серьёзнее то, как работают с этим вектором. Анализатор не может оперировать высокоуровневыми понятиями и заметить неладное, зато это может заметить человек и сказать всё, что думает про эту дичь.
Код ниже проверяет наличие этих тегов в строке. Но ведь в теге ещё есть открывающая угловая скобка<, поэтому ИИ, не стыдясь, создаёт эти временные строки, содержащие тег со скобкой:
Далее нужно проверить наличие этого тега в строке без учёта регистра. Собственно, почему бы для этого не создать ещё одну временную строчку. Вайб-код генерировать — не мешки ворочать.
Весь этот кринжвыглядитследующим образом:
В худшем случае (если тега в тексте нет),создаётся восемь временных строк!Ситуацию лишь немного спасает то, что формируемые строки небольшие по размеру, и Small String Optimization внутриstd::pmr::stringустранит динамические аллокации.
И ведь можно достаточно легко сделать код быстрее. Во-первых, можно сразу сформировать нужные префиксы и записать их в вектор. Во-вторых, вектор можно смело заменить на массивstd::string_view, что вовсе уберёт динамические аллокации.
В-третьих, теперь можно найти префикс с помощью написанной ранее функцииstarts_with_insensitive. В результате весь говнокод, приведённый выше, сокращается до:
Код стал в два раза короче и в N раз быстрее.
Можно и дальше улучшения делать. Например, не создавать строку с тегом в скобахend_condition = "" + tag + ">";, а брать уже готовую из массива. Но хватит уже это место мучить.
Цена красоты
Когда я первый раз пролистывал код, моё уважение вызвал код сериализации массива объектов в строку. Всё по фэншую: разные функции обходят с помощью паттерна “визитор” массивы вариантов и формируют нужные строки. Код типобезопасный и короткий.
Функций сериализации четыре:GetAltTextFromIds,GetAltText,RenderInlines,DebugAst, но для примера приведу только одну, которая покороче:
Выглядит элегантно. Изначально из-за этого кода я думал назвать статью как-то менее вызывающе. После рассмотрения недостатков я планировал привести этот код как положительный пример. Мол, вот здесь ИИ написал лучше, чем это сделает не очень начитанный C++ программист. Итого: местами не очень получилось, а где-то прям здорово.
Перед написанием хвалебной части мне, естественно, понадобилось посмотреть на код внимательнее и попробовать разобраться, что к чему. Зачем я только это сделал... Остался бы и дальше очарованным паттернами, ключевыми словамиconstexprи кодом, красиво оформленным в столбик.
Оказывается, и здесь плохо. Сейчас и вам расскажу почему.
Итак, стоит задача хранить массив неких элементов, которые по-разному сериализуются в строки.
Простым подходом будет создать базовый класс и наследовать от него разные сущности. В контейнере тогда будут храниться указатели на него. А для сериализации понадобится в разных случаях вызывать разные виртуальные функции. Получилась бы приблизительно следующая иерархия классов:
Массив таких объектов превращался бы в строку как-то так:
Приведённый код требует ручного управления памятью, да и вообще небезопасен. Ситуацию можно улучшить, храня в векторе умные указатели, напримерstd::unique_ptr. Другой вариант — использоватьstd::any.
Однако остаётся вторая проблема: код сериализации размазан по множеству классов. Можно собрать обработку в одном месте, узнавая тип объекта в массиве с помощьюdynamic_cast. Это медленно.
Поэтому классическим решением будет хранить идентификатор объекта в базовом классе, чтобы быстро определять его тип:
Теперь обработку, хоть и не очень изящно, можно собрать в одном месте:
Если хочется большего изящества, можно воспользоваться типом данныхstd::variant, что и сделал Claude Opus. Типstd::variant— это такой умный вариантunion, безопасный с точки зрения работы типов. Вот только тут есть нюанс. Жирный такой нюанс.
Но начнём мы с того, то ИИ вообще создал странную избыточную сущность. Он завёлперечислениеNodeType, которое бывает нужно для схемы, которую мы рассмотрели выше:
И вставил бессмысленный числовой идентификаторkTypeв структуры:
Это перечисление и идентификатор в структурах не нужны.kTypeнигде не используется, благо объявлен он какstaticи не занимает память в каждом объекте.
Само перечисление используется в единственном месте — в функцииNodeTypeToString:
Вот только непонятно, зачем и как этой функцией пользоваться на практике. Зачем она? Чем она поможет? Кажется, никому и ничем. В общем, всё это можно удалить и сократить размер кода проекта ещё строк на 100.
Ну лишний код, и ладно — не привыкать. Дальше нас ждёт самое толстое. В прямом смысле слова.
Особенность переменной типаstd::variantв том, что она должна иметь достаточный размер для размещения внутри себя наибольшей альтернативы. В нашем случае вариант должен вмещать в себя следующие классы:
Размер этих классов весьма разный, начиная с самых маленьких, таких какSoftBreak:
Размер этого класса 1 байт. Хотя класс пустой, он всё равно должен занимать хотя бы один байт.
Самый большой класс —Image:
Его размер в 64-битной программе может составлять 120 байтов. Дело в том, чтоstd::pmr::string, например, в реализацииSTLзанимает 40 байтов. Вы уже поняли, что это означает?
Вариант ещё дополнительно хранит информацию, какой тип он содержит в текущий момент. Итого размер одного объекта типаInlineNodeсоставляет 128 байтов! Можетездесь сами посмотреть.
Итого, программа работает с массивами, где размер каждого элемента 128 байтов. Офигительная “эффективность” расхода памяти.
Столько памяти нужно даже для того, чтобы хранить пустые классы, обозначающие разделители и превращающиеся обычно в пробелы:
Чтобы знать, где напечатать пробел, требуется хранить 128 байтов.
Перерасход памяти сам по себе, может, не такая уж большая проблема, но здесь ещё возникает и проблема производительности. При работе с таким растянутым массивом менее эффективно будет использоваться и кэш процессора.
Можно возразить, что вариант с умными указателями добавляет уровень косвенности при доступе к объекту, что тоже замедлит работу. И ещё нужно самому тип объекта проверять (см. выше пример соswitch).
Не принимается. Косвенный доступ по указателю не существенный, учитывая, что потом всё равно будет куча обращений к памяти для чтения строк и их конкатенации. Перерасход памяти выглядит куда более существенной проблемой. Что по поводу проверки типа объекта, так она всё равно есть, просто скрыта внутриstd::variant.
Ещё представьте, как болезненно происходит расширение вектора, если очередной элемент уже не влезает в зарезервированный буфер. Например, здесь:
При расширении вектора происходит перемещение объектов на новые места. Брр... Версия с умными указателями на базовый классstd::vector>куда легковеснее.
Итог: перед нами вновь говнокод, поданный в красивой обёртке.
Я не говорю, что умные указатели илиstd::anyздесь является лучшим решением. Можно что-то и другое придумать. Если задача — писать эффективный код, то это хорошее место, чтобы подумать, как хранить объекты и обходить их для сериализации.
Не знаю. Я и так уже вышел за лимит времени, который собирался посвятить просмотру кода и статье.
Это ещё раз говорит о росте ценности ревьюеров. Нагенерировать код всё проще. Вопрос: где взять силы на его просмотр и вынесение вердикта, получилось ли то, что нужно? Кто возьмёт ответственность за результат? Мы всё-таки в мире C++, суть использования которого — эффективность. Если кому-то скорость вообще не нужна, что он тут забыл?
Внимательный разбор завершаю, но напоследок пройдусь по некоторым предупреждениям PVS-Studio.
Предупреждение:V560[CWE-571] A part of conditional expression is always true: !input.empty(). markus.h 2228
ИИ педантично вставляет[[unlikely]], чтобы подсказать компилятору, что это маловероятное событие. Но при этом через некоторое время забывает, что далее строка всегда непустая, и влепляет бессмысленную повторную проверку!input.empty().
С кем поведёшься, от того и наберёшься
Предупреждение:V1048[CWE-1164] The ‘prev_line_had_content’ variable was assigned the same value. markus.h 4090
Переменнойprev_line_had_contentприсваивается значениеtrue, хотя из проверки и без того следует, что там лежит это значение. Анализатор прав — перед нами бессмысленная строчка кода. Но предупреждение неинтересное: я бы не стал его рассматривать, если бы не комментарий.
Слышали байки, что программисты иногда пишут верные, но бесполезные комментарии? Видимо, у ИИ были “хорошие” учителя. Это как раз тот случай, когда комментарий фактически пересказывает выполняемое действие, не добавляя ничего нового.
См. также "Вредный совет N53 — Отвечай в комментарии на вопрос “что?” в книге “60 антипаттернов для С++ программиста”.
Создание временных прокси объектов
Предупреждение:V823Decreased performance. Object may be created in-place in the ‘result’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2597
В начале создаётся временный объект типаText, затем он помещается в контейнер. Первая мысль: использоватьemplace_back:
Но изменение наemplace_backв этом случае даст примерно 0% ускорения, так ещё и нагрузит дополнительно компилятор — ему придётся делать инстанцирование шаблона функцииemplace_back. Внутри него уже будет идеально передаваться созданный объект, который будет перемещён конструктором перемещения.
Здесь придётся написать более хитрый код, чтобы действительно получилось размещение сразу в контейнере. Это контейнер вариантов, значит, мы должны подсказать ему, какую именно альтернативу собираемся создавать, и передать еёstd::string_view.
Вот как это будет выглядеть:
В таком случае мы идеально передадим подсказку о конструируемом типе и аргументы конструктора этого типа. Нас интересует5-я перегрузкаконструктораstd::variant.
При таком изменении мы избавимся от одного вызова конструктора перемещения.
- V823 Decreased performance. Object may be created in-place in the ‘result’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2610
- V823 Decreased performance. Object may be created in-place in the ‘result’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2632
- V823 Decreased performance. Object may be created in-place in the ‘result’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2650
- V823 Decreased performance. Object may be created in-place in the ‘result’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2733
- V823 Decreased performance. Object may be created in-place in the ‘result’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2751
- V823 Decreased performance. Object may be created in-place in the ‘result’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2754
- V823 Decreased performance. Object may be created in-place in the ‘result’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2855
- V823 Decreased performance. Object may be created in-place in the ‘result’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2931
- V823 Decreased performance. Object may be created in-place in the ‘quote_lines’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 5239
- V823 Decreased performance. Object may be created in-place in the ‘quote_lines’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 5241
- V823 Decreased performance. Object may be created in-place in the ‘item_lines’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 5641
- V823 Decreased performance. Object may be created in-place in the ‘item_lines’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 5643
- V823 Decreased performance. Object may be created in-place in the ‘para_lines’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 6045
- V823 Decreased performance. Object may be created in-place in the ‘para_lines’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 6047
- V823 Decreased performance. Object may be created in-place in the ‘delimiter_stack’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2729
- V823 Decreased performance. Object may be created in-place in the ‘delimiter_stack’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2746
- V823 Decreased performance. Object may be created in-place in the ‘line_offsets_’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2193
- V823 Decreased performance. Object may be created in-place in the ‘line_offsets_’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2196
- V823 Decreased performance. Object may be created in-place in the ‘line_offsets_’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2208
- V823 Decreased performance. Object may be created in-place in the ‘line_offsets_’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2218
- V823 Decreased performance. Object may be created in-place in the ‘line_offsets_’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2221
- V823 Decreased performance. Object may be created in-place in the ‘line_offsets_’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2229
- V823 Decreased performance. Object may be created in-place in the ‘result’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2637
- V823 Decreased performance. Object may be created in-place in the ‘result’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2947
- V823 Decreased performance. Object may be created in-place in the ‘result’ container. Consider replacing methods: ‘push_back’ -> ‘emplace_back’. markus.h 2958
Копия вместо перемещения
Предупреждение:V820The ‘content’ variable is not used after copying. Copying can be replaced with move/swap for optimization. markus.h 4670
Строка в переменнойcontentкопируется, хотя её можно переместить, так как дальше она не нужна. Можно написать:
А можно обойтись вообще без временного объектаheadingи написать что-то типа:
- V820 The ‘info_string’ variable is not used after copying. Copying can be replaced with move/swap for optimization. markus.h 4761
- V820 The ‘content’ variable is not used after copying. Copying can be replaced with move/swap for optimization. markus.h 4762
- V820 The ‘content’ variable is not used after copying. Copying can be replaced with move/swap for optimization. markus.h 5101
- V820 The ‘content’ variable is not used after copying. Copying can be replaced with move/swap for optimization. markus.h 5821
Закат солнца вручную
Предупреждение:V837The ‘insert’ function does not guarantee that arguments will not be copied or moved if there is no insertion. Consider using the ‘try_emplace’ function. markus.h 5274
PVS-Studio предупреждает о неэффективности, если в контейнере уже есть элемент с таким же ключом. В нашем случае защита от этого сделана вручную, но она неэффективна. В коде произойдут два поиска:
- Сначала ищем элемент по ключу для проверки условия.
- При вставке производим эту же операцию снова, пытаясь найти нужную позицию вставки в хэш-таблицу.
Вызовtry_emplaceв этой ситуации сделает ровно один поиск и вставит элемент, если его там не было:
Заключение
Выводы всё те же, что были в предыдущей статье “Давайте заглянем в этот самый вайб-код”. Независимо от того, как вы создаёте программный код, для обеспечения качества и надёжности разрабатываемого ПО следует использовать комплекс мер.
- Цените архитекторов и разработчиков, которые умеют декомпозировать и строить модульную архитектуру.
- Помните, что безопасность закладывается в систему с момента её проектирования (КИБ).
- Цените разработчиков, которые могут отличить эффективный код от медленного, безопасный от опасного. Тех, кто может разобраться и принять решение, имеет ли право созданный код стать частью проекта.
- Используйтестатические анализаторы кода.
- Используйтединамические анализаторы кодаиFAST анализаторыдля frontend-приложений.
- Используйтеинструменты композиционного анализа. В случае использования ИИ это особенно актуально, так как ихгаллюцинации начали применять для атак на цепочки поставок.
- Используйтестандарты кодирования. Нет ничего хуже, когда код написан вразнобой.
- Примените по необходимости другие практики, обобщённо называемыеразработкой безопасного ПО.
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Andrey Karpov.Let’s check vibe code that acts like optimized C++ one but is actually a mess.