Содержание статьи
- Ставим подходящий компилятор
- GCC
- LLVM
- Какой компилятор выбрать?
- Homebrew
- Дополнительные настройки компилятора
- Настраиваем VS Code
- Компилируем
- Запускаем и отлаживаем
- Ищем ошибки на лету
- Нововведения языка
- Поддержка
- Рефлексия
- Контракты (C++26)
- Обрабатываем контракты и ошибки
- Контролируем выполнение с помощью библиотеки std::execution
- Прочие изменения
- Выводы
C++ — успешный преемник языка C, который проник во все сферы, где есть информационные технологии, то есть буквально везде. Даже если завтра C++ заменит какой‑нибудь новомодный язык вроде Rust, Zig или Nim, поддержка всего софта на C/C++ останется актуальной.
Часто новые версии C++ заимствуют фичи у новомодных языков вроде перечисленных выше. Например, модули вместо заголовочных файлов, тип std::
, система владения (в виде умных указателей) и многопоточные абстракции заимствованы из Rust. Лямбды — из Lisp. C++ также перенял от Lisp и интерпретируемых языков, таких как Python и JavaScript, динамическую типизацию, которая выражена в std::
и std::
. Мне кажется, что заимствование удачных и проверенных решений не только ускоряет развитие языков, но и позитивно сказывается на выборе областей применения.
21 июня 2025 года комитет ISO C++ завершил работу над проектом развития языка C++26. Его спецификация готова: в него включены все востребованные функции, и изменений больше не будет. Теперь разработчики компиляторов могут спокойно приступать к реализации нового стандарта в коде.
info
На утверждение документа потратили неделю упорной работы 200 специалистов на заседании комитета ISO C++ в Софии, Болгария. Большинство присутствовало лично, остальные подключались через Zoom. Всего в мероприятии приняли участие представители 30 стран. По словам Герба Саттера, принятый стандарт оказался больше по объему, чем все предыдущие за последние 20 лет.
Новый стандарт языка C++ выходит каждые три года. Однако действительно новые фичи появляются раз в шесть лет, а промежуточные версии — это что‑то вроде работы над ошибками. Например, предыдущий крупный выпуск — это C++20, а C++23 стал исправлением ошибок предшественника. Грядущий C++26 снова предложит сильно обновленный язык.
Ядро языка обычно остается стабильным, но вот стандартная библиотека часто подвергается серьезным изменениям и добавлениям новых функций. Например, интеллектуальные указатели — это всего лишь шаблоны стандартной библиотеки.
Ставим подходящий компилятор
Прежде чем обсуждать нововведения языка, давай разберемся с тестовой средой, в которой можно будет проверять эти возможности.
В последней версии компилятора Microsoft Visual C++, который входит в Visual Studio 2022 (версия 17.14), до сих пор отсутствует полная поддержка C++23. Что уж говорить о поддержке C++26!
На выбор остаются два других компилятора: GCC и Clang. Оба примерно на две трети поддерживают стандарт C++26, хотя Clang обычно получает обновления раньше. Это связано с фундаментальным устройством этих компиляторов.
GCC
GCC — это монолитная система, в которой фронтенд, внутренний платформенно независимый язык и бэкенд работают как единый процесс. Фронтенд обрабатывает высокоуровневый язык, выполняет его лексический и синтаксический анализ и преобразует его во внутренний язык передачи регистров — RTL. GCC поддерживает фронтенды для языков C, C++, Objective-C, Fortran и многих других.
Затем подключается подходящий бэкенд, который преобразует код с языка RTL в машинный код для выбранной архитектуры: x86, x86-64, ARM, MIPS, SPARC, PowerPC и других. Кстати, GCC поддерживает наибольшее количество бэкендов, обеспечивая генерацию кода для множества архитектур. Это делает GCC ведущим компилятором для почти всего системного ПО. Если твой код компилируется под одну архитектуру, то с большой вероятностью он соберется с GCC и под другие.
Заметь, что GCC — это довольно старый софт. Ричард Столлман выпустил его еще в 1987 году, и с тех пор сообщество открытого ПО непрерывно развивает GCC.
LLVM
Clang — это фронтенд для языков C/C++, Objective-C/C++ внутри фреймворка LLVM. Хотя LLVM расшифровывается как Low Level Virtual Machine, виртуальной машиной в привычном понимании он не является. Скорее LLVM напоминает платформу для разработки компиляторов, похожую на GCC.
Главное отличие от GCC — модульная архитектура: каждый слой LLVM — это отдельный механизм. Это позволяет быстро вносить изменения в инфраструктуру и развивать проект, добавляя поддержку новых языков программирования (фронтендов) и новых аппаратных архитектур (бэкендов). В качестве внутреннего кросс‑платформенного языка LLVM использует промежуточное представление — IR. Благодаря модульной архитектуре LLVM развивается быстрее, чем GCC, и даже вытесняет его.
info
Разработка фреймворка LLVM началась в 2000 году в Университете Иллинойса. К середине второго десятилетия XXI века фреймворк привлек большое внимание в IT-отрасли и стал широко использоваться ведущими компаниями: Apple, Adobe, AMD, Google, Nvidia и другими.
Какой компилятор выбрать?
Clang в большинстве случаев компилирует быстрее, чем GCC, благодаря своей модульной архитектуре. Такая структура делает Clang/LLVM идеальным для создания инструментов, IDE и специализированных утилит для кодинга. Также Clang известен более информативными сообщениями об ошибках во время компиляции по сравнению с GCC. Однако GCC дает лучшую оптимизацию благодаря множеству механизмов, реализованных в виде флагов компиляции, таких как -Wall
, -Wextra
и другие, которые помогают выявлять потенциальные проблемы.
Нельзя однозначно выбрать один компилятор — все зависит от нужд конкретного проекта. Чем больше сравниваешь, тем сложнее принять решение. В macOS я обычно пользуюсь Clang, так как он стандартный в Xcode для трансляции кода на C++. Мне особенно интересно, как возможности C++26 будут работать на процессоре семейства Apple Silicon. Однако версия компилятора C++ по умолчанию в Xcode для наших целей не подходит.
Я не собираюсь настаивать на своем выборе, так что можешь взять любую Unix-подобную систему. Затем предлагаю установить последнюю версию Clang и LLVM. Для этого используй Homebrew — этот менеджер пакетов работает как на macOS, так и в Linux. Но для начала проверим, какая версия у тебя уже установлена.
Поскольку на моей системе установлен Xcode с обычной для него версией Clang, команда clang
, выполненная в командной строке, показывает
Apple clang version 17.0.0 (clang-1700.0.13.5)
Target: arm64-apple-darwin24.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
warning
Перед установкой LLVM не забудь поставить последнюю версию Python, если он еще не установлен. На момент написания статьи актуальной версией была 3.13.5. Python необходим для установки LLVM.
Homebrew
Чтобы установить менеджер потерянных пакетов, выполни в консоли такую команду:
/bin/zsh -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"(https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)")
Она сработает независимо от того, используешь ты Linux или macOS.

Установим свежую версию Clang/LLVM через Homebrew:
brew install llvm
По умолчанию будет установлена последняя доступная версия 20.1.8. Ее номер не связан с версией стандарта языка, так как у Clang/LLVM своя система нумерации. Это именно то, что нужно.
Делаем установленный компилятор первым в списке (в переменной среды):
echo 'export PATH="/opt/homebrew/opt/llvm/bin:$PATH"' >> ~/.zshrc
Применяем настройки:
source ~/.zshrc
Вновь выполняем:
clang –v
Смотрим результат:
Homebrew clang version 20.1.8
Target: arm64-apple-darwin24.5.0
Thread model: posix
InstalledDir: /opt/homebrew/Cellar/llvm/20.1.8/bin
Configuration file: /opt/homebrew/etc/clang/arm64-apple-darwin24.cfg
System configuration file directory: /opt/homebrew/etc/clang
User configuration file directory: /Users/yurembo/.config/clang
Дополнительные настройки компилятора
Для нового компилятора нужно переопределить пути к каталогам заголовочных и объектных файлов. Иначе система будет использовать их из каталога по умолчанию:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Чтобы не прописывать каждую строчку вручную через командную строку, открой конфиг в любом текстовом редакторе (например, в VS Code). Для этого, находясь в домашнем каталоге, выполни
code .zshrc
Вставь в открывшийся файл две строки, приведенные ниже. Они указывают на каталоги включаемых и объектных файлов для недавно установленного компилятора:
export LDFLAGS="-L/opt/homebrew/opt/llvm/lib"export CPPFLAGS="-I/opt/homebrew/opt/llvm/include"

Теперь по умолчанию будет использоваться новый компилятор. Возникает вопрос: почему Apple не интегрирует его в свою среду разработки сразу? Ответ прост: чтобы защитить пользователей от возможных багов и недоработанных функций.
info
Скорее всего, тебе понадобится компоновщик lld
. Раньше он устанавливался вместе с LLM, но в новых версиях его нужно устанавливать отдельно командой brew
.
Начнем с проверки работоспособности нового компилятора, скомпилировав программу из командной строки. Для примера возьмем простой код, демонстрирующий работу класса std::
, добавленного в C++20. Главное отличие std::
от std::
из C++11 в том, что jthread
позволяет останавливать поток в любое время, не дожидаясь завершения выполнения связанного с ним алгоритма.
#include <chrono>#include <iostream>#include <thread>using namespace std::literals;int main() { std::cout << '\n'; std::jthread nonInterruptible([]{ int counter{0}; while (counter < 10){ std::this_thread::sleep_for(0.2s); std::cerr << "nonInterruptible: " << counter << '\n'; ++counter; } }); std::jthread interruptible([](std::stop_token stoken){ int counter{0}; while (counter < 10){ std::this_thread::sleep_for(0.2s); if (stoken.stop_requested()) return; std::cerr << "interruptible: " << counter << '\n'; ++counter; } }); std::this_thread::sleep_for(1s); std::cerr << '\n'; std::cerr << "Main thread interrupts both jthreads" << '\n'; nonInterruptible.request_stop(); interruptible.request_stop(); std::cout << '\n';}
Сохраним и скомпилируем этот файл:
clang++ -std=c++26 jthread.cpp -o jthread
Если ты правильно выполнил все шаги по установке и настройке, программа успешно скомпилируется. Запусти ее командой ./
.
Программа создает и сразу запускает через лямбда‑функции, используя класс std::
, два потока: nonInterruptible
и interruptible
. Второй поток можно прервать по запросу. Оба потока выполняют схожий алгоритм: каждый запускает цикл, который завершается после десяти итераций. В начале цикла поток засыпает на 200 мс, затем просыпается и выполняет две или четыре операции в зависимости от назначения.
Прерываемый поток проверяет, получен ли параметр std::
с запросом stop_requested
, и, если это так, завершает свою работу. Две общие операции для обоих потоков: вывод названия потока вместе со значением счетчика и увеличение самого счетчика.
После запуска потоков основная программа засыпает на одну секунду; в это время потоки продолжают работать. Затем она выводит текстовую строку и отправляет обоим потокам запросы на остановку, но только прерываемый поток реагирует на это и завершает выполнение, в то время как непрерываемый продолжает работу. Это хорошо видно в выводе программы.

Настраиваем VS Code
Существует множество редакторов кода, и выбор — это вопрос личный и почти религиозный. Я использую VS Code и всем советую, но перед использованием для кодинга на C++ его нужно немного настроить.
Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее