C++Architecture

C++20 Modules & Modern Build Systems: Replacing #include, CMake Integration, and 10x Faster Builds

TT
TopicTrick Team
C++20 Modules & Modern Build Systems: Replacing #include, CMake Integration, and 10x Faster Builds

C++20 Modules & Modern Build Systems: Replacing #include, CMake Integration, and 10x Faster Builds


Table of Contents


The #include Problem: Why Builds Are Slow

mermaid

Problems with #include:

  1. Re-parse on every TU: Same headers parsed by every file that includes them
  2. Order sensitivity: Include guards work, but #define leaks across files
  3. Macro pollution: Macros from one header infect all subsequent headers
  4. No encapsulation: All names (exported or not) pollute the global namespace
  5. Implicit dependencies: If file.cpp uses std::string, but only includes via a chain of other headers, it compiles today but breaks tomorrow

Module Anatomy: Export Module, import, and Ownership

A module consists of one or more module units — translation units that declare themselves part of the module:

cpp
cpp

Key differences from #include:

  • import math reads a pre-compiled Binary Module Interface (BMI/IFC) — not source text
  • Non-exported names are completely invisible to importers (true encapsulation)
  • Macros defined inside the module do NOT leak to importers
  • Multiple imports of the same module are instant (BMI cached)

Module Partitions: Splitting Large Modules

Large modules can be split into partitions — sub-units that belong to the same module:

cpp
cpp

import std: The Standard Library Module (C++23)

C++23 standardizes importing the entire standard library as a module:

cpp

Header Units: Incremental Migration Path

Header units let you import existing headers without converting them to modules:

cpp

CMake 3.28+: Native Module Support

cmake

msvc (Visual Studio) — .ixx files:

text

clang with Ninja (fastest):

bash

Build Time Benchmarks: #include vs Modules

text

Toolchain Support Matrix (2026)

FeatureMSVC 19.34+Clang 17+GCC 14+
Basic export/import
Module partitions
import std;⚠️ Partial
Header units
CMake integration

Frequently Asked Questions

Can I mix modules and headers in the same project? Yes — and this is the recommended migration strategy. You can #include legacy headers inside module interface units (the includes are private to the module), and importers never see the polluted namespace. Migrate module by module over time.

Do modules break ODR (One Definition Rule)? Modules actually improve ODR compliance. The compiler tracks module membership and rejects cases where the same entity is defined in multiple modules with different definitions. With headers, the same ODR violation causes undefined behavior silently.

Are module file extensions standardized? No — .cppm (Clang), .ixx (MSVC), and .mpp are all common. CMake 3.28+ and the NDK use .cppm by convention. The extension doesn't matter — the export module declaration is what identifies a file as a module interface unit.


Key Takeaway

C++20 Modules are the most significant change to C++ code organization since the language was created. They eliminate the 50-year-old #include textual inclusion model and replace it with a compiled binary interface system that scales to millions of lines of code. The build time improvements (10-30×) are just the most visible benefit — true encapsulation (macros don't leak, internals are invisible) and improved ODR safety are equally important. In 2026, any new C++ project should default to modules with import std;.

Read next: Embedded C++ & Safety-Critical Engineering →


Part of the C++ Mastery Course — 30 modules from modern C++ basics to expert systems engineering.