C++20模块用export module声明接口单元、import替代#include,实现编译模型重构;接口单元导出内容供外部使用,未export的默认私有,import必须位于翻译单元最前面。
用 C++20 模块(Modules)替代头文件,核心就两点:用 module 声明接口单元,用 import 替代 #include。它不是语法糖,而是编译模型的重构——模块被单独编译成二进制接口文件(如 .pcm / .ifc),不再重复解析、宏污染或依赖顺序问题。
模块分接口单元(interface unit)和实现单元(implementation unit)。接口单元导出供别人用的内容,必须以 export module 开头:
export module math_utils;
export int add(int a, int b) { return a + b; }
export namespace math {
constexpr double PI = 3.14159;
}
注意:
• export 只修饰你想对外暴露的声明或定义(函数、变量、类、命名空间等)
• 不加 export 的内容默认私有,仅在本模块内可用
• 模块名(math_utils)是逻辑标识,不要求与文件名一致,但建议保持一致便于维护
用 import,不是 #include,且必须出现在翻译单元最前面(不能在头文件里、不能在 #include 后面):
import math_utils;
int main() {
return add(2, 3); // OK
// std::cout << math::PI; // 需要额外 import(见下条)
}
常见组合:
• import std; —— 导入标准库统一模块(C++23 起更成熟,C++20 多数编译器仍实验性支持)
• import —— 导入传统头文件的“头文件单元”(需编译器开启对应选项,如 Clang 的 -fmodules-ts 或 MSVC 的 /experimental:module)
• import "my_header.h"; —— 导入自定义头文件作为模块(需先将其显式构建为头文件单元)
模块不是“写了就能跑”,编译流程变了:
clang++ -std=c++20 -x c++-system-header -fmodules -fimplicit-modules -fimplicit-module-maps -c math_utils.cppm -o math_utils.ifc
clang++ -std=c++20 -fmodules -fmodule-file=math_utils.ifc main.cpp -o main
cl /std:c++20 /experimental:module /EHsc main.cpp math_utils.ixx,它自动处理依赖关键提醒:
• 所有使用模块的源文件必须用相同标准(如都用 -std=c++20)
• 模块接口文件路径需被正确识别(Clang 常需 -fmodule-map-file 或 -fprebuilt-module-path)
• 不同编译器对模块的支持程度不同:MSVC 最早落地,GCC 11+ 支持较稳,Clang 需 13+ 且配置较细
不能立刻全切。建议分三步走:
utils.h 改成 utils.cppm)特别注意:模板定义必须在接口单元中可见(不能像头文件那样只声明),所以模块里写模板函数/类时,定义通常得和声明一起 export 出来。