C++20 Modules
Modules 是 C++20 对传统头文件机制的革命性替代,解决了编译速度慢、宏污染、符号重复定义等历史遗留问题。
头文件的问题
cpp
// 传统头文件的痛点:
// 1. 每个翻译单元都要重新解析头文件(编译慢)
// 2. 宏会泄漏到包含者的命名空间
// 3. 顺序依赖(include 顺序影响行为)
// 4. 符号重复定义(需要 include guard)
// 5. 隐式依赖(通过间接 include 获得符号)
// math.h 可能定义了宏 min/max,污染你的代码
#include <windows.h> // 定义了 min/max 宏
#include <algorithm> // std::min/max 被宏破坏!基本模块语法
cpp
// === math.cppm(模块实现文件)===
export module math; // 声明模块名
// 导出函数
export int add(int a, int b) { return a + b; }
export double sqrt_approx(double x) { return x; } // 简化
// 不导出的内容(模块内部实现)
static int helper() { return 42; } // 不可见
// 导出类
export class Vector2D {
public:
double x, y;
Vector2D(double x, double y) : x(x), y(y) {}
double length() const;
};
// 导出命名空间
export namespace math {
constexpr double PI = 3.14159265358979;
int gcd(int a, int b);
}cpp
// === main.cpp(使用模块)===
import math; // 导入模块,不是 #include
int main() {
auto result = add(3, 4); // 7
auto v = Vector2D(3.0, 4.0);
auto pi = math::PI;
// helper() 不可见,编译错误
}模块分区(Module Partitions)
cpp
// === geometry-shapes.cppm(模块分区)===
export module geometry:shapes; // geometry 模块的 shapes 分区
export struct Circle { double r; };
export struct Rectangle { double w, h; };
// === geometry-algorithms.cppm(另一个分区)===
export module geometry:algorithms;
import :shapes; // 导入同模块的 shapes 分区
export double area(const Circle& c) { return 3.14159 * c.r * c.r; }
export double area(const Rectangle& r) { return r.w * r.h; }
// === geometry.cppm(主模块接口)===
export module geometry;
export import :shapes; // 重新导出 shapes 分区
export import :algorithms; // 重新导出 algorithms 分区
// === main.cpp ===
import geometry; // 获得所有导出的内容
Circle c{5.0};
std::cout << area(c) << "\n";导入标准库(C++23)
cpp
// C++23 标准库模块
import std; // 导入整个标准库
import std.compat; // 包含 C 兼容头文件
int main() {
std::vector<int> v = {1, 2, 3};
std::cout << std::ranges::max(v) << "\n";
}
// C++20 过渡期:头文件单元
import <vector>; // 将头文件作为模块导入(编译器支持)
import <iostream>;CMake 集成
cmake
# CMakeLists.txt(CMake 3.28+)
cmake_minimum_required(VERSION 3.28)
project(MyProject)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
add_executable(app main.cpp)
target_sources(app
PUBLIC
FILE_SET CXX_MODULES FILES
math.cppm
geometry.cppm
)与头文件互操作
cpp
// 在模块中使用传统头文件
module; // 全局模块片段(Global Module Fragment)
// 在这里 #include 传统头文件,不会导出宏
#include <cmath>
#include <string>
export module mylib; // 模块声明从这里开始
// 现在可以使用 std::string、std::sqrt 等
export std::string format_number(double x) {
return std::to_string(std::sqrt(x));
}编译速度对比
传统头文件:
main.cpp → 解析 <vector> → 解析 <algorithm> → ... → 编译
每个 .cpp 文件都重复解析所有头文件
Modules:
math.cppm → 编译一次 → 生成 .pcm(预编译模块)
main.cpp → 直接读取 .pcm → 编译
实测:大型项目编译速度提升 2-10x关键认知
Modules 是 C++ 编译模型的根本性改进。新项目建议直接使用 Modules,老项目可以通过"全局模块片段"逐步迁移。C++23 的 import std; 让标准库导入变得极其简单。