模块不是头文件的语法糖,必须先编译接口再导入,且export/import写法、顺序及构建配置三者缺一不可:module声明须为首行非空非注释语句,import须全局且在#include前,模块名大小写敏感,CMake需显式配置编译顺序与BMI路径。

模块不是头文件的语法糖,用错就编译失败或链接报错——必须先编译接口、再导入,且 export 和 import 的写法、顺序、构建配置三者缺一不可。
module 声明必须在文件首行(跳过空白和注释)
编译器只认翻译单元最开头的 export module 或 module,哪怕前面有一行空格、一个 // comment 或 #pragma once,都会报 error: module declaration not at global scope。
-
export module math;必须是.ixx或.cppm文件的第一条非空非注释语句 - 不能写成
#include后再写export module math;,也不允许前置宏定义 - MSVC 强制要求接口文件后缀为
.ixx(否则不识别为模块),Clang/GCC 推荐用.cppm,但都靠命令行参数而非后缀判断是否为模块
import 必须全局、且在所有 #include 之前
import 是编译期符号绑定,不是文本包含;它依赖已生成的二进制模块接口(BMI/PCM/IFC),且解析顺序严格:一旦出现 #include,后续 import 在 Clang/GCC 下会警告,在 MSVC 下可能静默失效。
- 正确写法:
import math;<br>#include <iostream></iostream>
- 错误写法:
#include <iostream><br>import math; // GCC/Clang 可能报 warning: import after #include</iostream>
- 模块名大小写敏感,
import Math;和import math;是两个不同模块 - 标准库模块如
import std;或import可直接用(取决于编译器支持程度),但旧头文件如"utils.h"不能import "utils.h",只能#include或封装成新模块
export 只控可见性,不解决 ODR 问题
export 不等于“安全多处定义”——它只是让符号对外可见;如果多个模块导出同名非 inline 全局变量,链接时仍会报 multiple definition。
- 导出函数定义可直接写:
export int add(int a, int b) { return a + b; }(隐含inline) - 导出全局变量必须显式加
inline:export inline int counter = 0; - 不要
export static int x;或export namespace { int y; }——这些无法被外部访问,且可能引发链接失败 - 想透传依赖模块的接口,用
export import core;,而不是把core里的东西再 copy 一遍export
CMake 中模块编译顺序和属性必须手动对齐
即使代码全对,CMake 没配对也会导致 module file not found——这不是语法错,而是构建系统没把 BMI 路径暴露给导入者。
- 必须开启 C++20:
set(CMAKE_CXX_STANDARD 20)且set(CMAKE_CXX_STANDARD_REQUIRED ON) - 模块接口文件要设属性:
set_source_files_properties(math.ixx PROPERTIES MODULE_INTERFACE ON) - MSVC 需加
/interface编译选项,Clang 需加-fmodules-cache-path=build/modules,GCC 需-fmodules-ts -fmodules-cache-path=build/modules - 主目标必须显式依赖模块目标:
target_link_libraries(myapp PRIVATE math_module)(若模块单独建 target)
最容易被忽略的是:模块接口文件必须比所有 import 它的源文件**先开始编译**。CMake 默认不保证这点,不显式声明依赖或设置 MODULE_INTERFACE ON,就大概率静默失败——报错信息里甚至不会提“顺序”,只说找不到模块。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











