C++ 进阶(28):模块系统——C++20 modules 基础、声明、可见性
更新时间:2026-09-01。本文是
languages/cpp/intermediate/进阶第 28 篇,接 协程实战。C++20 引入了模块(modules),替代传统的头文件 + 源文件模式。声明更清晰,编译更快,避免宏污染。
本文要回答的问题
- 模块是什么?和传统头文件有什么不同?
- 怎么声明模块?怎么导出接口?
- export 和 import 的可见性规则是什么?
- 模块分区的实际好处是什么?
一、模块 vs 传统头文件
cpp
// 传统头文件:math_utils.h
#pragma once
int add(int a, int b); // 声明
#define PI 3.14 // 宏定义,会污染cpp
// 传统源文件:math_utils.cpp
#include "math_utils.h"
int add(int a, int b) { return a + b; }cpp
// 模块:math_utils.cpp(C++20)
export module math_utils; // 声明模块
export int add(int a, int b) {
return a + b;
}
const double PI = 3.14; // 不导出,外部不可见二、模块声明
cpp
// 单个文件模块:math_utils.cpp
export module math_utils;
// 导出函数
export int add(int a, int b) {
return a + b;
}
export int multiply(int a, int b) {
return a * b;
}
// 不导出,只在模块内部使用
int internal_helper() {
return 42;
}cpp
// 使用模块:main.cpp
import math_utils; // 导入模块
int main() {
return add(1, 2);
}三、export 和 import 规则
cpp
export module my_module;
// 导出函数
export int func();
// 导出类
export class MyClass {
public:
int method();
};
// 导出类型别名
export using MyVector = std::vector<int>;
// 导出模板
export template<typename T>
T max(T a, T b) { return a < b ? b : a; }
// 导出命名空间
export namespace MyNS {
int value();
}可见性:
export修饰的:外部可见- 没
export的:模块内部可见,如同 C 的 static 函数 - 不需要
#include保护,没有宏污染
四、模块分区
cpp
// 主模块声明:math.cpp
export module math;
export import :utils; // 导出子模块
// 子模块:math-utils.cpp
export module math:utils; // 分区名为 utils
export int helper(int x) {
return x * 2;
}五、模块的好处
| 对比 | 传统头文件 | 模块 |
|---|---|---|
| 编译速度 | 每个 include 都要解析 | 模块只编译一次,缓存 |
| 宏污染 | #define 污染所有 includes | 模块不导出宏 |
| 顺序依赖 | 头文件包含顺序重要 | 模块无顺序依赖 |
| 私有接口 | 头文件里需要声明 | 模块内部默认不导出 |
| 循环依赖 | 容易出 | 模块不允许循环依赖 |
六、编译
bash
# 编译模块
g++ -std=c++20 -fmodules-ts -c math_utils.cpp
g++ -std=c++20 -fmodules-ts -c main.cpp
g++ -std=c++20 main.o math_utils.o七、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 模块里的宏不导出 | 调用方看不到宏 | 宏不适合模块,用内联函数或 constexpr 替代 |
| 不同编译器模块格式不兼容 | 无法交叉使用 | 同一编译器编译 |
| 模块不允许循环依赖 | 编译错误 | 重新设计模块结构 |
| 模块名需唯一 | 和别的模块冲突 | 用命名空间格式,如 myapp.math |
相关与延伸
下一篇:Ranges 基础——C++20 范围库入门;C++20 模块系统深入,见 模块系统深入。
一句话总结
C++20 模块:替代头文件+源文件模式,export module 声明模块,export 导出接口,import 导入模块;模块内部不导出的东西外部不可见,不需要宏保护和顺序依赖;模块编译一次后缓存,比传统头文件快;不支持宏、不支持循环依赖;不同编译器格式不兼容,需要在同一编译器下编译。