## 探索C++20 模块化:从头到尾实现一个简单的模块化库

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. 进一步优化与实践

  1. 分离接口与实现

    • my_math.cppm 仅包含接口声明。
    • 新建 my_math_impl.cppm,实现函数并使用 module my_math;
  2. 模块依赖

    • 其它模块可以通过 import my_math; 来复用。
  3. 编译缓存

    • 模块对象文件 *.o 可在多次编译中复用,极大减少编译时间。
  4. IDE支持

    • 现代IDE(CLion、Visual Studio、VS Code + Clangd)对模块支持已趋成熟,方便可视化调试。

7. 小结

  • 模块化解决了头文件的二次编译与命名冲突。
  • 接口文件.cppm)负责声明。
  • 实现文件可同文件也可分离。
  • 通过 import 引入模块即可像使用普通命名空间一样使用。

随着C++20标准的完善与编译器生态的成熟,模块化将成为日益重要的代码组织手段,建议在新项目中尝试使用,享受更快的编译与更干净的代码。

发表评论