C++20引入了模块化(Modules)概念,旨在解决传统头文件的编译时间长、重复包含以及命名空间污染等痛点。下面我们从零开始实现一个最小的模块化库 my_math,并在主程序中演示如何导入与使用。
1. 创建模块接口文件 my_math.cppm
// my_math.cppm
export module my_math; // 声明模块名
import <cmath>; // 仅在接口文件中使用
export namespace my_math {
// 计算两数之和
export int add(int a, int b);
// 计算欧氏距离
export double distance(double x1, double y1, double x2, double y2);
}
2. 在同一文件中实现模块实现
C++20支持在同一文件中直接实现接口声明的内容,或者单独创建实现文件。这里我们在同一文件里实现。
int my_math::add(int a, int b) {
return a + b;
}
double my_math::distance(double x1, double y1, double x2, double y2) {
double dx = x2 - x1;
double dy = y2 - y1;
return std::sqrt(dx * dx + dy * dy);
}
说明:如果你想把实现放在单独的文件
my_math_impl.cppm,需要使用module my_math;而不是export module my_math;。
3. 编译模块
使用支持C++20模块的编译器(如 GCC 13+、Clang 16+、MSVC 19.34+)。编译命令示例:
# 生成模块对象文件
g++ -std=c++20 -fmodules-ts -c my_math.cppm -o my_math.o
# 编译主程序
g++ -std=c++20 -fmodules-ts main.cpp my_math.o -o demo
-fmodules-ts是开启模块特性的实验性标志,实际编译器版本请根据官方文档确认。
4. 主程序 main.cpp
// main.cpp
import my_math; // 导入模块
#include <iostream>
int main() {
int sum = my_math::add(7, 15);
double dist = my_math::distance(0.0, 0.0, 3.0, 4.0);
std::cout << "7 + 15 = " << sum << '\n';
std::cout << "Distance from (0,0) to (3,4) = " << dist << '\n';
return 0;
}
5. 运行结果
$ ./demo
7 + 15 = 22
Distance from (0,0) to (3,4) = 5
6. 进一步优化与实践
-
分离接口与实现
- 将
my_math.cppm仅包含接口声明。 - 新建
my_math_impl.cppm,实现函数并使用module my_math;。
- 将
-
模块依赖
- 其它模块可以通过
import my_math;来复用。
- 其它模块可以通过
-
编译缓存
- 模块对象文件
*.o可在多次编译中复用,极大减少编译时间。
- 模块对象文件
-
IDE支持
- 现代IDE(CLion、Visual Studio、VS Code + Clangd)对模块支持已趋成熟,方便可视化调试。
7. 小结
- 模块化解决了头文件的二次编译与命名冲突。
- 接口文件(
.cppm)负责声明。 - 实现文件可同文件也可分离。
- 通过
import引入模块即可像使用普通命名空间一样使用。
随着C++20标准的完善与编译器生态的成熟,模块化将成为日益重要的代码组织手段,建议在新项目中尝试使用,享受更快的编译与更干净的代码。