Wiki
Introduction to modules
Existing English translations were reused. 3 current source blocks remain in Simplified Chinese, without triggering paid translation.
1.What problem does this section solve?
Traditional #include has three major problems:
- Slow compilation:Every
.cppfile must reparse all header files included via include (possibly tens of thousands of lines). - Macro pollution:
#defineaffects all code after the include. - Order dependency:The order of includes may affect program behavior.
Modules are a new feature introduced in C++20 that fundamentally solve these problems—faster compilation, better isolation, and no macro leakage.
2.What is this feature?
Modules is the module system introduced in C++20. Unlike #include (text copy-paste), modules are precompiled interface declarations that export only the parts you want to expose, while the internal implementation is completely hidden.
3.C++ Standard Edition
C++20 (officially introduced), C++23 enhanced the import std; standard library module.
4.Required header files
Modules do not require header files. Module files typically use the .cppm extension (community convention) or .ixx (MSVC).
5.Basic syntax
// math_module.cppm —— 模块接口文件
export module math; // 声明模块名
export int add(int a, int b) // export:对外可见
{
return a + b;
}
int multiply(int a, int b) // 没有 export:模块内部可见
{
return a * b;
}
// main.cpp —— 使用模块
import math; // 导入模块(替代 #include)
int main()
{
int x = add(3, 5); // ✅ 可以访问
// int y = multiply(3, 5); // ❌ 不可访问(没有 export)
return 0;
}
Run result: The program terminates normally, and the terminal has no additional output.
6.Core Concepts
| 概念 | 说明 |
|---|---|
export module 模块名; |
声明一个模块(放在模块接口文件中) |
export |
标记对外可见的函数/类/变量 |
import 模块名; |
导入一个模块 |
import <头文件>; |
导入传统头文件(把它当作模块来用,编译器支持有限) |
| 模块接口单元 | .cppm 文件,包含 export module |
| 模块实现单元 | .cpp 文件,包含 module 模块名;(不带 export) |
7.The core difference between include and module
Do not understand modules as simply "replacing #include with import". The two have different compilation models, so the problems they can solve are also different.
| Comparison point | #include |
import module |
|---|---|---|
| Working method | Copy text, paste the header file contents into the current file | Import an already-compiled module interface |
| Visibility | Whatever is written in the header file is basically visible to includers | Only the name of export is visible externally |
| Macros | Macros tend to leak outward | Module interfaces do not propagate macros the way text inclusion does |
| Compilation dependency | Include order may affect the result | Module dependencies are clearer |
| Build requirements | All compilers and build systems support it maturely | Requires relatively new compilers and build systems |
So at this stage, when learning about modules, the focus is on understanding that "the separation of interface and implementation is more thorough." Whether to migrate in a real project depends on whether the toolchain supports it stably.
8.Example code
8.1.Example 1: The simplest module
Document 1:math_module.cppm(Module Interface)
export module math;
export int add(int a, int b)
{
return a + b;
}
export int subtract(int a, int b)
{
return a - b;
}
// 内部函数,不对外暴露
int internal_helper()
{
return 0;
}
File 2:main.cpp
import math;
#include <iostream>
int main()
{
std::cout << "add(3, 5) = " << add(3, 5) << "\n";
std::cout << "subtract(10, 3) = " << subtract(10, 3) << "\n";
// internal_helper(); // ❌ 编译错误!没有 export
return 0;
}
运行结果:见下方“运行结果”;模块示例需要先按编译命令生成模块接口,再运行 main.cpp。
Compilation command (using GCC as an example):
# 先编译模块接口
g++ -std=c++20 -fmodules-ts -c math_module.cppm -o math_module.o
# 再编译主程序
g++ -std=c++20 -fmodules-ts -c main.cpp -o main.o
# 链接
g++ math_module.o main.o -o program
Running result:
add(3, 5) = 8
subtract(10, 3) = 7
8.2.Example 2: Module + Class
File:person_module.cppm
export module person;
import <string>;
import <iostream>;
export class Person
{
std::string name_;
int age_;
public:
Person(const std::string& name, int age)
: name_(name), age_(age) {}
void print() const
{
std::cout << name_ << ", age " << age_ << "\n";
}
};
File:main.cpp
import person;
int main()
{
Person p("Alice", 25);
p.print();
// p.name_ = "Bob"; // ❌ 编译错误!name_ 是 private
return 0;
}
Running result:
Alice, age 25
8.3.Example 3: Based on Example 2, separate the interface from the implementation.
Examples 1 and 2 put the implementation directly in the module interface for clarity. A more common approach in real projects is: the interface unit exports only declarations, and the implementation unit contains the function bodies.
Document 1:counter.cppm(Module Interface Unit)
export module counter;
export class Counter
{
int value_ = 0;
public:
void add(int n);
int value() const;
};
Document 2:counter.cpp(Module Implementation Unit)
module counter;
void Counter::add(int n)
{
value_ += n;
}
int Counter::value() const
{
return value_;
}
Document 3:main.cpp
import counter;
#include <iostream>
int main()
{
// 程序从 main 函数开始执行,下面的语句会按顺序运行。
Counter c;
c.add(3);
c.add(5);
std::cout << "counter = " << c.value() << "\n";
// 返回 0 表示程序正常结束。
return 0;
}
Running result:
counter = 8
9.Operation result
See the "run results" for each example above.
10.Key syntax explanation in the example
| Example | What was discussed | Newly introduced syntax | Why it is written this way | Notes |
|---|---|---|---|---|
| Example 1 | Basic module definition and import | export module、import、export |
A module only exposes what is exported | There is no hard rule for module file names,.cppm is a community convention |
| Example 2 | Exporting classes from a module | export class、import <string> |
Class public/private is still valid | import <header> belongs to a header unit, and toolchain support varies significantly |
| Example 3 | Separating interface units and implementation units | module counter; |
The interface exposes only declarations, while the implementation is placed in a separate file | The build system needs to know module dependencies |
11.Common Errors
Error 1: Compiler not supported or not enabled
g++ -std=c++17 main.cpp # ❌ C++17 没有 modules
Correct approach: use -std=c++20 -fmodules-ts (GCC/Clang), MSVC uses /std:c++20.
Error 2: Forgot export
// module.cppm
export module my_module;
int func() { return 42; } // ❌ 没有 export,外部看不到!
Correct approach:export int func() { return 42; }
Error 3: Incorrect compilation order
The module interface unit (.cppm), must be compiled first before compiling files that use the module.
Correct approach: use CMake 3.28+ or a build system to manage module dependencies.
Mistake 4: Treating private as the entirety of module hiding
export module demo;
export class A
{
private:
int value_;
};
int helper(); // 没有 export,外部看不到
private controls the access permissions of class members;export controls whether module-level names are visible externally. The two solve different problems.
12.Usage Recommendations
- 现阶段可以了解但不强制迁移:C++20 modules 的编译器支持仍在完善中,传统
#include在相当长时间内仍是主流。 - 新项目可以尝试:如果使用最新编译器(GCC 15+、Clang 17+、MSVC 2022+),modules 已比较稳定。
- CMake 3.28+ 支持 modules:
target_sources(myapp PUBLIC FILE_SET CXX_MODULES FILES ...)。 - 标准库模块
import std;是 C++23 的特性:C++20 只能 import 自定义模块。 - 理解 modules 的设计思想很重要:它代表了 C++ 的未来方向。
- 教程里先学概念,不强背命令:不同编译器的模块命令差异很大,实际项目交给 CMake/构建系统管理更稳。
13.Summary
- Modules are a new compilation model for C++20, replacing
#include, being faster and safer. export moduledeclares the module name, andexportmarks declarations visible externally.importimports modules (replacing#include).- A module interface unit is responsible for exposing the API, while a module implementation unit is responsible for hiding the implementation.
- Compiler support is continuously improving; it can be understood, but its use in current projects requires evaluation.
- Learning the design of modules helps in understanding the modularization concepts of large C++ projects.