Modules通过预编译接口为二进制.pcm文件并共享AST与符号信息,避免重复解析和模板实例化;需编译器显式启用模块模式、正确配置路径与后缀,并注意标准库模块支持限制及导入顺序约束。

Modules 为什么能大幅减少重复编译?
传统头文件包含(#include)本质是文本复制:每次编译 .cpp 文件,预处理器都会把所有 #include 的头文件内容原样塞进去,再交给编译器处理。这意味着:vector、string、Boost 某个组件,只要被 100 个源文件包含,它们的模板实例化、宏展开、语法解析就重复执行 100 次。
Modules 把接口和实现分离成二进制中间表示(.pcm 文件),编译器只需导入一次模块,后续直接复用已解析好的 AST 和符号信息。这不是“避免头文件”,而是彻底跳过预处理+重解析阶段。
关键点:模块接口单元(module interface unit)只编译一次,生成的 .pcm 被所有导入它的翻译单元共享。
Clang 17 / MSVC 19.38 下启用 Modules 的最小可行步骤
不是所有编译器都默认支持,也不是加个 import 就行——必须显式开启模块模式并指定输出路径。
立即学习“C++免费学习笔记(深入)”;
- Clang(Linux/macOS):用
-x c++-system-header预编译标准库头为模块,再通过--precompile和--modules-cache-path控制缓存;主模块需用-fmodules -fimplicit-modules -fimplicit-module-maps - MSVC(Windows):项目属性 → “常规” → “C++ 语言标准” 设为
/std:c++20或更高;“C++ 模块支持”设为启用;并在命令行追加/experimental:module /module:interface /module:output mymod.pcm - 必须将模块接口文件(如
mymodule.ixx)加入工程;.ixx后缀是 MSVC 强制要求,Clang 推荐用.cppm,但需配合-x c++-module
import 和 #include 混用时的陷阱
不能简单把 #include 替成 import —— 标准库模块尚未全平台稳定提供,且模块映射(module map)必须存在。
常见错误现象:error: module 'std' not found 或 import declaration not allowed in this context。
- MSVC 可通过
/experimental:standard-modules启用实验性标准模块,此时import才有效;否则必须先用#include+export module mymod;封装一层 - Clang 需手动创建
module.modulemap并用-fmodule-map-file=指向它,否则无法识别import "xxx" - 一旦某个
.cpp文件用了import,该文件**必须**以模块方式编译(即带-fmodules),且不能混用传统头文件包含与模块导入在同一 TU 中(会触发module import must appear before any declarations)
真实项目中提速 90% 的前提条件
“减少 90% 重复编译”只在特定结构下成立:大量源文件共享同一组重型头文件(如自研框架基类、序列化层、跨平台抽象),且这些头文件改动频率极低。
如果项目里每个 .cpp 都只包含 和几个小头文件,或模块接口频繁修改导致 .pcm 大量失效,加速效果会急剧下降。
性能影响要点:
-
.pcm文件本身较大(一个封装了QtCore的模块可达 200MB),首次构建可能更慢,但后续增量编译受益明显 - 模块依赖图必须清晰,循环
import会导致编译失败,而头文件的隐式依赖往往掩盖问题 - CMake 需升级到 3.27+,并用
target_compile_features(... PRIVATE cxx_modules)+set_property(SOURCE xxx.ixx PROPERTY CXX_MODULE_KIND interface)
最易被忽略的一点:模块不解决链接时间问题,也不减少模板代码膨胀;它只砍掉重复解析和实例化前端开销。如果你的瓶颈在 ld 阶段或内存耗尽,Modules 帮不上忙。










