C++20 Modules: Escape Include Hell and Speed Up Builds with import
이 글의 핵심
Why modules beat repeated header parsing: module units, export blocks, partitions, build commands, performance tables, and production migration patterns with PIMPL and CI caching.
Introduction: “Including headers makes builds too slow”
One large header pulls in dozens more; every .cpp parses that text again. Modules parse once and reuse BMIs (e.g. .pcm)—major compile-time wins at scale.
Requirements: C++20, GCC 11+, Clang 13+, MSVC 2019 16.10+; CMake 3.28+ has solid FILE_SET ... MODULES support.
What is a module?
A module is a translation unit with an export module (or implementation module name;) that exposes only exported declarations to import consumers—unlike textual #include paste.
Global module fragment
module;
#include <vector> // only in this preamble
export module mylib;
export int add(int a, int b) { return a + b; }
Header vs module (concept)
| #include | import | |
|---|---|---|
| Unit | Text paste | Parsed BMI cache |
| Duplicate work | Per TU | Once per module |
| Macros | Leak everywhere | Contained |
Partitions
Split large modules: export module mylib:part1;, aggregate with export import mylib:part1; in the primary interface.
Build commands (sketch)
- GCC:
-std=c++20 -fmodules-ts, compile interface units first in dependency order. - Clang: often
-std=c++20 -c file.cppm. - MSVC:
/std:c++20and/interfacefor.ixx.
CMake 3.28+
target_sources(app PRIVATE FILE_SET all MODULES
math.cppm
geometry.cppm
geometry_impl.cpp
)
Common errors
- Module not found / link order—build interface
.obefore importers. exportinsidemodule;preamble—illegal.- Using non-exported symbols from another module.
- Circular imports—split interfaces or introduce a third module.
#includebeforeimportcan pollute macros—preferimportfirst.- Wrong extension—use
.cppm/.ixxfor interface units. - Wrong partition compile order.
exportin implementation unit—only primary/partition interfaces export.
Performance (illustrative)
Large projects often see ~50–75% compile-time reduction versus repeated header parsing—depends on project shape and compiler.
Production patterns
- Migrate new code first; keep
#includewhere needed. - PIMPL + modules for ABI stability.
- Wrap
import stdgaps with thin module shims until the toolchain supportsimport std. - Cache
.pcmpaths in CI.
Related posts
- Module migration #24-2
- Modules overview
- VS slow build
Next: Module migration #24-2
Previous: Async coroutines #23-3