Модули в C++20: Почему пора навсегда забыть про #include

Аватар пользователя admin
пт, 07/03/2026 - 10:19 -- admin

До выхода C++20 сборка проектов принципиально не менялась около сорока лет. Мы послушно тащили за собой наследие языка C: пилили код на заголовочные файлы (.h / .hpp) и файлы реализации (.cpp), а потом склеивали их через текстовую вставку #include.

С появлением C++20 эта эпоха наконец-то уходит. На смену неудобным хедерам пришли Модули (Modules). Давайте разберемся, почему старый подход всех достал, как устроена новая система и как запустить свой первый модуль.

В чем проблема старого доброго #include?

Когда вы пишете #include iostream, препроцессор не делает никакой магии. Он буквально берет весь текст этого гигантского файла (вместе со всеми его внутренними зависимостями) и вставляет его в ваш .cpp файл прямо перед компиляцией.

Из-за этого разработчики постоянно спотыкались о три проблемы:

  • Компиляция со скоростью улитки. Если один и тот же популярный хедер подключен в 50 разных файлов проекта, компилятор будет честно разбирать его текст все 50 раз. В больших проектах это выливается в часы мучительного ожидания сборки.
  • Война макросов. Если в каком-нибудь стороннем хедере прописан макрос #define max 100, он без спроса пролезет во все файлы, куда импортирован этот заголовок. И если у вас где-то была своя переменная или функция max, код внезапно и очень загадочно сломается.
  • Хрупкий порядок подключения. Стоит случайно поменять местами #include "A.h" и #include "B.h", как проект может рассыпаться с сотней невнятных ошибок просто потому, что один файл неявно зависел от объявлений в другом.

Модули в C++20 решают эти болячки разом. Они компилируются в бинарный формат всего один раз, не тормозят сборку соседних файлов, изолируют макросы внутри себя и не зависят от того, в каком порядке их подключают.

Анатомия модуля: три главных ключевых слова

Чтобы работать с модулями, в синтаксис C++ добавили новые зарезервированные слова. Всё предельно логично:

  • export module имя_модуля; — этой строчкой мы говорим компилятору, что данный файл является модулем.
  • export — маркер видимости. Ставится перед классами, функциями или переменными, которые мы хотим отдать «наружу».
  • import имя_модуля; — подключает готовый модуль там, где мы хотим его использовать.

Пишем первый модуль своими руками

Давайте создадим простую библиотеку для математических функций и вызовем ее в основной программе. Больше никаких .h файлов!

Шаг 1. Создаем интерфейс модуля

Создайте файл с расширением .cppm (универсальный стандарт для большинства компиляторов) или .ixx (если работаете чисто в Visual Studio). Назовем его MathUtils.cppm.

// Объявляем модуль с именем MathUtils
export module MathUtils;
 
// Подключаем стандартную библиотеку в модульном стиле
import iostream; 
 
// Эта функция будет доступна везде, где импортирован модуль
export int add(int a, int b) {
    return a + b;
}
 
// Эта функция тоже открыта для внешнего мира
export void printMessage() {
    std::cout  << "Привет из модуля MathUtils!" << std::endl;
}
 
// А вот эта функция НЕ экспортируется. 
// Она останется скрытой деталью реализации внутри модуля.
void secretInternalFunction() {
    // Внутренняя логика...
}

Шаг 2. Подключаем модуль в основной программе

Теперь создаем обычный файл main.cpp. Обратите внимание: никаких хедеров нам больше не нужно.

// Подключаем наш собственный модуль
import MathUtils; 
 
int main() {
    // Спокойно вызываем функции из модуля
    printMessage();
 
    int result = add(5, 7);
 
    // Ошибка компиляции! Функция secretInternalFunction() не экспортировалась,
    // поэтому из main.cpp мы ее не видим.
    // secretInternalFunction(); 
 
    return 0;
}

Сравнение: старая школа против новой

Критерий Старый #include Новый import
Как работает Тупо копирует текст файла «как есть» Подключает скомпилированный бинарный интерфейс
Скорость сборки Медленно (парсинг повторяется многократно) Молниеносно (компилируется 1 раз)
Защита от макросов Отсутствует (макросы утекают и ломают код) Полная изоляция внутри модуля
Инкапсуляция Всё, что есть в .h файле, торчит наружу Видно только то, что помечено словом export

Как пощупать модули прямо сейчас?

Современные компиляторы уже отлично дружат с модулями, но иногда им нужно немного помочь настройками.

  • В Visual Studio (MSVC): Создайте C++ проект, зайдите в Свойства проекта -> Свойства конфигурации -> Общие. Убедитесь, что в поле «Стандарт языка C++» стоит ISO C++20 (или новее). Студия автоматически поймет, что файлы .ixx — это модули.
  • В GCC / Clang: Для сборки пока еще нужно явно указывать флаги. Например: -std=c++20 -fmodules-ts.

Резюме: Модули — это глоток свежего воздуха для экосистемы C++. Они избавляют от бесконечной возни с заголовками, ускоряют сборку и делают архитектуру чище. Если стартуете новый проект или пишете библиотеку с нуля — самое время оставить #include в прошлом.