Модули в C++20: практическое руководство для начинающих
Модули в C++20 — это современный способ организации кода, который приходит на смену директиве #include. Они решают проблемы дублирования, скрытия деталей реализации, ускоряют сборку и уменьшают риск конфликтов имён и макросов. Ниже — практическое руководство: что такое модуль, как его написать, собрать и использовать.
Что такое модули в C++20
export module имя; и содержит объявления того, что вы хотите экспортировать.module имя;. Здесь можно держать детали, которые не видны снаружи.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> до тех пор, пока ваша цепочка инструментов не подтвердит полноценную поддержку.
Частые ошибки и их решения
constexpr и enum class вместо #define.export, тем стабильнее интерфейс и быстрее сборка.Лучшие практики при работе с модулями в C++20
#include оставьте как есть; позже выделяйте модули-обёртки.Когда модули дают максимальную выгоду
Итоги
Модули в C++20 делают код чище, сборку быстрее, а архитектуру — надёжнее. Начните с одного небольшого модуля, подключите CMake 3.28+ и постепенно переносите доступные части проекта на modules. Это даст вам реальный прирост производительности сборки и улучшит читаемость кода.
Хотите системно прокачать основы и современный C++ (включая C++20/23) на практике? Рекомендую пошаговый курс «Программирование на C++ с Нуля до Гуру» — отличный способ быстро закрыть пробелы и научиться писать промышленный код.