Модули в C++20: практическое руководство для начинающих

Модули в C++20: практическое руководство для начинающих

Модули в 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++ с Нуля до Гуру» — отличный способ быстро закрыть пробелы и научиться писать промышленный код.

    Источник

    НЕТ КОММЕНТАРИЕВ

    Оставить комментарий