Модули в C++20: практическое руководство для начинающих
Модули в C++20 — это современный способ организации кода, который приходит на смену директиве #include. Они решают проблемы дублирования, скрытия деталей реализации, ускоряют сборку и уменьшают риск конфликтов имён и макросов. Ниже — практическое руководство: что такое модуль, как его написать, собрать и использовать.
Что такое модули в C++20
- Модульный интерфейс (interface unit) — точка входа в модуль. Объявляется строкой
export module имя;и содержит объявления того, что вы хотите экспортировать. - Модульная реализация (implementation unit) — файл с определениями. Объявляется строкой
module имя;. Здесь можно держать детали, которые не видны снаружи. - Разделы (partitions) — позволяют разбить интерфейс на части:
export module имя:часть;и подключать их из основного интерфейса.
Ключевая идея: внешнему миру вы показываете только то, что пометили export, а всё остальное надёжно инкапсулировано внутри модуля.
Минимальный пример: модуль math
Создадим простой модуль с парой функций и константой. Принято использовать расширения .ixx или .cppm для интерфейса.
math.ixx (интерфейс)
export module math;
export int add(int a, int b);
export double pi();
math.cpp (реализация)
module math;
double pi() { return 3.141592653589793; }
int add(int a, int b) { return a + b; }
main.cpp
import math; // Подключаем модуль
#include <iostream> // Можно оставить обычные заголовки (надёжнее, чем header units)
int main() {
std::cout << "add(2, 3) = " << add(2, 3) << "\n";
std::cout << "pi = " << pi() << "\n";
}
Сборка через CMake 3.28+ (рекомендуется)
Самый простой кросс-платформенный путь — использовать CMake с поддержкой FILE_SET CXX_MODULES. Он сам правильно вызовет компилятор для интерфейсов и реализаций.
cmake_minimum_required(VERSION 3.28)
project(cpp20_modules_demo LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(app main.cpp)
# Объявляем модульные файлы как FILE_SET CXX_MODULES
target_sources(app
PRIVATE
FILE_SET cxx_modules TYPE CXX_MODULES FILES
math.ixx
PRIVATE
math.cpp
)
Дальше стандартно: cmake -S . -B build && cmake --build build. Понадобятся современные компиляторы: MSVC 19.3x+, Clang 17+, GCC 13+ (степень поддержки может отличаться).
Интерфейсные разделы (partitions)
Если интерфейс разрастается, вынесите часть API в раздел и "переэкспортируйте" его из основного интерфейса.
math.ops.ixx (интерфейсный раздел)
export module math:ops; // Раздел интерфейса
export int mul(int a, int b);
math.ops.cpp (реализация раздела)
module math:ops;
int mul(int a, int b) { return a * b; }
math.ixx (обновлённый интерфейс модуля)
export module math;
export import :ops; // Переэкспорт API из раздела
export int add(int a, int b);
export double pi();
main.cpp
import math;
#include <iostream>
int main() {
std::cout << mul(4, 5) << "\n"; // из раздела ops
std::cout << add(2, 3) << "\n";
}
В CMake добавьте раздел аналогично интерфейсу:
target_sources(app
PRIVATE
FILE_SET cxx_modules TYPE CXX_MODULES FILES
math.ixx
math.ops.ixx
PRIVATE
math.cpp
math.ops.cpp
)
Header Units: когда можно писать import <iostream>
Некоторые компиляторы поддерживают header units — импорт стандартных заголовков как модулей: import <iostream>;. Это ускоряет сборку, но пока не везде стабильно и одинаково. Для кросс-платформенных проектов безопаснее оставить обычный #include <iostream> до тех пор, пока ваша цепочка инструментов не подтвердит полноценную поддержку.
Частые ошибки и их решения
- "Не удаётся найти BMI/PCM" — сборка модулей идёт в два шага: интерфейс компилируется первым, создаётся метаданный артефакт (BMI/IFC/PCM), затем компилятся зависящие единицы. Используйте CMake 3.28+ или корректные флаги компилятора; не пытайтесь компилировать всё одним вызовом без знания порядка.
- Смешение макросов и модулей — макросы не пересекают границы модулей. Избегайте зависимости экспортируемого API от макросов; используйте
constexprиenum classвместо#define. - Случайные "утечки" деталей реализации — экспортируйте только то, что действительно нужно пользователю. Чем меньше
export, тем стабильнее интерфейс и быстрее сборка. - Импорт циклических модулей — проектируйте зависимости как направленный ациклический граф. Вынесите общие типы в отдельный модуль-ядро.
Лучшие практики при работе с модулями в C++20
- Держите интерфейс «тонким»: минимально необходимый набор экспортов, без лишних зависимостей.
- Стабилизируйте API: изменения интерфейса требуют пересборки пользователей модуля, как и в обычных библиотеках.
- Избегайте глобального состояния в интерфейсе; инициализацию переносите в функции/фабрики.
- Переходите постепенно: сначала модули для нового кода, старые
#includeоставьте как есть; позже выделяйте модули-обёртки. - Для кросс-платформы используйте CMake 3.28+ и современные компиляторы (MSVC, Clang, GCC) в актуальных версиях.
Когда модули дают максимальную выгоду
- Большие проекты со множеством заголовков (ускорение инкрементальных сборок).
- Библиотеки с чётким публичным API и обширной внутренней реализацией.
- Команды, где важна строгая изоляция и контроль над зависимостями.
Итоги
Модули в C++20 делают код чище, сборку быстрее, а архитектуру — надёжнее. Начните с одного небольшого модуля, подключите CMake 3.28+ и постепенно переносите доступные части проекта на modules. Это даст вам реальный прирост производительности сборки и улучшит читаемость кода.
Хотите системно прокачать основы и современный C++ (включая C++20/23) на практике? Рекомендую пошаговый курс «Программирование на C++ с Нуля до Гуру» — отличный способ быстро закрыть пробелы и научиться писать промышленный код.
-
Создано 15.05.2026 17:01:14
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.