По данным отраслевых опросов, в 2025 году 84% разработчиков использовали ИИ-инструменты для написания, отладки и автоматизации кода. А в 2026 году ожидается, что более 80% компаний будут применять генеративный ИИ в разработке своих продуктов. Но у LLM есть ограничения, которые не позволяют им быть одинаково эффективными для всех языков программирования — например, для С++.
Тему ограничений AI в пайплайне «плюсовой» разработки обсудили Андрей Золотых и Илья Казаков из YADRO, Константин Владимиров из Синтакора, Илья Шишков из Сбертеха и Денис Фокин из LRI. Читайте, к каким выводам пришли эксперты, и поделитесь своим мнением в комментариях: есть ли место ИИ-агентам в разработке на «плюсах»?
C++ — это язык со статической типизацией, сложной моделью времени жизни объектов и широким набором стандартов. Ошибки в нем проявляются иначе, чем в Python или JavaScript: не как немедленная остановка выполнения кода, а в виде нарушений инвариантов и неопределенного поведения.
Поэтому случайный фрагмент Python-кода со StackOverflow с высокой вероятностью просто запустится. А «рандомный» пример на C++ может скомпилироваться, а затем повести себя некорректно — из-за управления временем жизни и памятью, скрытого неопределенного поведения, а также из-за зависимости от контекста сборки: стандарт, флаги компилятора, ABI, версии библиотек.
По этой же причине ответы языковых моделей для C++ требуют более строгой валидации: сгенерированный код может выглядеть аккуратно и без проблем компилироваться, но содержать скрытые дефекты, которые проявятся уже во время выполнения или позже в редких сценариях и под нагрузкой.
Разработчик на С++ всегда помнит о двух вещах: сколько живет объект и что обязано его уничтожить. Если объекта уже нет, но остались указатель или ссылка на него, мы получим use‑after‑free и Undefined behavior. ИИ-ассистент при генерации кода такие детали нередко упускает.
В отличие от языков, где есть механизм Garbage collector, в C++ именно программист решает, когда освобождать память и другие ресурсы. RAII и стандартные обертки — умные указатели, контейнеры и scope-guards — помогают реализовать это в коде, но разработчику все равно важно точно понимать, где начинается и где заканчивается жизнь объекта. Такие детали LLM часто оставляет «без внимания», поэтому может легко пропустить ошибки.
LLM не знает о многочисленных стандартах C++, которые используют разные идиомы и предполагают разные возможности стандартной библиотеки и компилятора. Языковая модель, обученная на наборе репозиториев разных стандартов, не всегда попадает в актуальный, поэтому она не может учитывать доступные возможности.
Все это создает ситуацию, в которой синтаксически корректный код оказывается концептуально неправильным. Поэтому участие LLM в проекте без надлежащего надзора разработчика может привести к следующим рискам.
ИИ может сгенерировать код, который будет компилироваться, использовать современные конструкции, и даже сопроводит написанное разумными комментариями. Но для C++ этого недостаточно: код должен не только собираться, но и соответствовать семантике проекта.
LLM может написать удобный на вид API, который незаметно для разработчика приведет к множественным копиям больших структур:
void log(std::initializer_list<Big> items); Big a, b; log({a, b}); // элементы initializer_list — это const, поэтому Big копируется и move не работает
В коде выше есть проблема: элементы std::initializer_list<T> всегда являются константными (const T), поэтому даже временные объекты не перемещаются.
Модель также не отслеживает владение ресурсами и время жизни объектов, поэтому может «забыть» про std::move или вызвать delete там, где он нерелевантен.
int x = 10; int* p = &x; delete p; // UB: удаляем не-heap объект
ИИ-ассистенты не знают о принципе работы инвариантов, поэтому, вопреки семантике и архитектуре проекта, могут предложить использовать простой int вместо strongly‑typed enum.
Пример:
// 0 = Down, 1 = Up void set_admin_state(int state); set_admin_state(2); // компилируется, но «третье» состояние, которого не существует
Правильно сделать так:
enum class AdminState : uint8_t { Down, Up }; void set_admin_state(AdminState s); set_admin_state(AdminState::Up);
Значительная часть логики C++ реализуется на этапе компиляции. SFINAE позволяет исключить неподходящие шаблоны из набора перегрузок при ошибке подстановки и тем самым выражать ограничения через систему типов. LLM знают синтаксические паттерны SFINAE, но не моделируют процесс разрешения перегрузок и инстанцирования шаблонов.
Или, например, в C++20 появились концепции (concept) для проверки требований к типам. Сгенерированный код может создавать слишком строгие ограничения к типам или, наоборот, создавать слабые concept, которые будут пропускать нежелательные типы.
Слишком слабый concept пропускает лишнее:
#include <concepts> template<class T> concept Multipliable = requires(T a, T b) { a * b; }; auto multiply(Multipliable auto a, Multipliable auto b) { return a * b; } bool b1 = true; bool b2 = false; multiply(b1, b2); // Компилируется! bool преобразуется в int → результат int, а не bool
Для корректной работы шаблонов критичны typename, template, правила поиска имен (lookup) и ADL (argument-dependent lookup). Ошибка в этих тонкостях в сгенерированном коде может привести к неожиданному выбору перегрузки, взрыву инстанцирования (превышению глубины шаблонов) или незаметно изменить смысл уже существующего кода.
Это касается не только C++, но и любого другого языка программирования — полностью сгенерированный код не должен попадать в продакшен без глубокого ревью. Ответственность за функционирование проекта все еще лежит на разработчике. Ее не получится делегировать на языковую модель, обученную на открытых кодовых базах. Это здравый смысл: ряд компаний уже провели эксперимент по делегированию работы разработчиков ИИ-ассистентам — все обернулось крахом, большими убытками и критическими ошибками.
Что касается конкретно C++, LLM не стоит использовать как справочник по стандартам. Ответы ИИ-ассистентов об устройстве памяти, модели исключений, порядке инициализации глобальных объектов, гарантированных оптимизациях компилятора могут быть просто неверны, и проверить их гораздо сложнее, чем факт из документации, особенно когда речь идет про UB, порядок вычислений, атомики и ODR. Если разработчик использует такие ответы, то ответственность не лежит на модели: она не отвечает за свои возможные ошибки или неточности.
Лучше всего использовать LLM как помощника — привлекать его к генерации шаблонного кода или для переезда на другие языки. Модель можно встроить в уже существующий процесс оценки качества, делегируя ей написание юнит-тестов.
Надо правильно относиться к ИИ и обозначить базовые правила при работе с ним: «ИИ‑сгенерированный код — всегда требует ревью» или «нельзя ссылаться на LLM как на аргумент в архитектурных решениях».
Можно зайти и с другой стороны. Мы лучше понимаем какие-то вещи, когда пытаемся объяснить их другим, а принцип работы LLM заключается именно в обучении. После объяснения какой-то концепции ИИ-ассистенту, вы поймете ее лучше и — как приятный бонус — получите одобрительное «you’re absolutely right».
LLM также может стать замечательным собеседником. Попробуйте отрефлексировать с ИИ-ассистентом код, если обычная уточка уже не помогает.
В рамках дискуссии «AI-ассистенты в разработке на C++: панацея или опасный костыль» на System Level Meetup эксперты также обсудили:
как QA-инженеры начали делать качественные багфиксы с помощью ИИ,
какие есть success story интеграции LLM в разработку на C++,
как относятся к сгенерированному коду в open source и многое другое.
Запись можно посмотреть на Rutube и YouTube.
Источник


