在 C++20 之前,头文件(header)是实现模块化的主要手段,但它们导致重复编译、全局符号污染以及难以维护的大型项目。C++20 引入了真正的模块(module)机制,解决了这些问题,并为编译器提供了更高效的构建策略。下面我们从最基础的概念入手,逐步实现一个最小化的模块示例,并演示如何在项目中使用它。
1. 模块概念回顾
- 模块接口单元(interface unit)
用 export module 声明,向外部暴露符号。
- 模块实现单元(implementation unit)
用 module 声明,不能直接导出符号,只能被同一模块内部使用。
- 模块单元
模块文件中可以包含多条 export 语句,也可以包含普通的 #include 或 #import(C++20 新增)。
2. 项目结构
/project
├─ src/
│ ├─ math/
│ │ ├─ math.module
│ │ └─ math.cpp
│ ├─ main.cpp
├─ build/
├─ CMakeLists.txt
math.module 用来声明模块名。
math.cpp 实现模块接口。
main.cpp 使用模块。
3. 代码实现
3.1 math.module
// src/math/math.module
export module math; // 公开模块名
export import std.core; // 公开 std.core 以便后续使用
export import std.core; 让所有 export 的符号都可以直接使用 `
`、“ 等标准库。
#### 3.2 math.cpp
“`cpp
// src/math/math.cpp
module math; // 同一模块实现单元
import
; // 必须使用 import 而不是 #include
export namespace math {
// 计算两点距离的函数
inline double distance(double x1, double y1, double x2, double y2) {
return std::sqrt((x2 – x1) * (x2 – x1) + (y2 – y1) * (y2 – y1));
}
// 一个简单的数学工具类
export class Calculator {
public:
double add(double a, double b) const { return a + b; }
double sub(double a, double b) const { return a – b; }
double mul(double a, double b) const { return a * b; }
double div(double a, double b) const { return a / b; }
};
}
“`
注意:
– `export namespace math`:将整个命名空间导出,内部的所有符号都可以被外部访问。
– `export class Calculator`:类也被导出。
– `import
`:在模块实现单元中使用 `import` 而非 `#include`。
#### 3.3 main.cpp
“`cpp
// src/main.cpp
import math; // 直接导入模块
#include
// 可以直接使用 std::cout 等
int main() {
using namespace math;
Calculator calc;
std::cout << "3 + 4 = " << calc.add(3, 4) << '\n';
double d = distance(0, 0, 3, 4);
std::cout << "Distance (0,0)-(3,4) = " << d << '\n';
return 0;
}
“`
—
### 4. CMake 配置
“`cmake
cmake_minimum_required(VERSION 3.23)
project(ModularCppExample LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 生成编译数据库,便于 IDE 识别模块
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# 模块单元
add_library(math SHARED src/math/math.cpp)
# 可选:指定编译器选项,让编译器正确处理模块
target_compile_options(math PRIVATE
$<$:/experimental:module>
$<$:-fmodules-ts>
$<$:-fmodules-ts>
)
# 主要可执行文件
add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE math)
“`
> **提示**
> – 对于 GCC/Clang,需要开启 `-fmodules-ts`。
> – MSVC 在 VS 2022(17.4+)已经支持模块,使用 `/experimental:module`。
—
### 5. 编译与运行
“`bash
mkdir build && cd build
cmake ..
cmake –build .
./app
“`
输出:
“`
3 + 4 = 7
Distance (0,0)-(3,4) = 5
“`
—
### 6. 小结
– **模块化带来的好处**
– 编译速度提升:模块只编译一次,之后的编译只需解析接口。
– 作用域更严格:仅暴露 `export` 的符号,避免全局污染。
– 依赖关系更清晰:`import` 明确标识依赖,而不是宏观的 `#include`。
– **注意事项**
– C++20 模块尚未在所有编译器中稳定实现,建议使用最新版本。
– 模块与传统头文件并存,迁移时可逐步替换。
通过以上示例,你可以在自己的项目中快速试验 C++20 的模块功能,为代码库的可维护性与构建效率奠定基础。祝编码愉快!