Skip to content

Ch 31: import 模块系统

  • 理解 import 语法和模块语义
  • 掌握 export module 声明
  • 学会模块分区和模块命名空间
  • 理解模块与头文件的区别和互操作
  • 理解当前编译器支持状态
# Python - import 模块/包
import os # 导入标准库
from collections import deque # 导入特定符号
import mypackage.mymodule # 导入子模块
from mypackage import func # 导入特定函数
# as 别名
import numpy as np
from pandas import DataFrame as DF
// C++20 import - 导入命名空间或模块
import std; // 导入 std 命名空间(未来 C++26)
import std.core; // 导入 std 的 core 子模块(未来)
import std.vector; // 导入 std::vector(未来)
// 当前(2024):只能 import 已声明的模块
import mymodule; // 导入自定义模块
import :helpers; // 导入分区(如果模块支持)
// 不能 import 标准库(大多数编译器不支持)
// import <vector> // 错误语法
#include <vector> // 当前只能用 #include
方面Python importC++ import
目标模块/包命名空间或模块单元
解析时机运行时(lazy)编译时
符号查找自动需 using 或限定
冲突处理as 别名作用域限定
// 基本模块声明
export module MyModule; // 导出整个模块
module MyModule; // 仅内部模块(不导出)
// 模块名称规则:
// - 必须是有效标识符或用 : 分隔的标识符序列
// - 不能使用点号(.),用冒号(:)表示层次
export module math; // OK
export module myapp.core; // OK (模块分区)
export module myapp::core; // 等价
export module std; // 保留给标准库
// ===== greetings.cpp - 模块实现文件 =====
module; // 可选,表示这是模块接口
export module greetings;
// 模块内私有部分(不影响外部)
module privately;
void private_helper() {
// 仅模块内部使用
}
// 导出函数
export std::string greet(const std::string& name) {
return "Hello, " + name + "!";
}
export int add(int a, int b) {
return a + b;
}
// 导出类
export class Calculator {
public:
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
};
// ===== main.cpp - 使用模块 =====
import greetings;
int main() {
std::cout << greet("World") << "\n"; // Hello, World!
std::cout << add(1, 2) << "\n"; // 3
Calculator calc;
std::cout << calc.multiply(3, 4) << "\n"; // 12
}
// ===== mymath.cppm - 接口文件(.cppm 是常用扩展名)=====
module;
export module mymath;
// 导出声明
export int multiply(int a, int b);
export double divide(double a, double b);
// 导出类型
export class Matrix {
public:
Matrix(int rows, int cols);
// ...
};
// ===== mymath.cpp - 实现文件 =====
module mymath; // 导入自己的模块(非导出)
// 实现
int multiply(int a, int b) {
return a * b;
}
// 也可以在实现文件中导出(inline)
export int another_func() { return 42; }
// 主模块
export module foo;
// foo:detail 分区
export module foo:detail; // foo 的 detail 子模块
// foo:impl 分区
export module foo:impl;
export void hidden_helper() {
// 仅 foo 内部使用
}
// 内部函数(不导出)
namespace foo {
namespace detail {
void internal_func() { }
}
}
main.cpp
import foo; // 导入主模块
import foo:detail; // 导入 detail 分区(如果需要)
import foo:impl; // 导入 impl 分区(如果需要)
// 使用
foo::some_exported_function();
foo::detail::helper_function(); // 可能不可见(取决于导出)
network.cppm
// ===== 大型库的模块划分 =====
export module network;
export void connect(const std::string& host);
export void disconnect();
// network:types.cppm
export module network:types;
export class Socket { /* ... */ };
export struct Address { /* ... */ };
// network:impl.cppm
module network:impl;
// 实现细节,不导出
class SocketImpl { /* 平台特定实现 */ };
// import - 导入整个模块/命名空间
import std; // 导入 std 命名空间(未来)
// using - 声明使用特定符号
using std::cout; // 只导入 cout
using namespace std; // 导入整个 std(不推荐)
// import vs using 的区别
import std;
void func() {
cout << "Hello\n"; // 直接用 cout(通过 import std)
// std::cout 仍然有效
}
// a.cpp - 模块 A
export module A;
export void helper() { }
// b.cpp - 模块 B
export module B;
export void helper() { }
// main.cpp
import A;
import B;
int main() {
// helper() 存在歧义 - 哪个模块的?
// A::helper(); // 需要限定
// B::helper();
A::helper(); // 明确指定
}
// C++20 允许将头文件转为 "header unit"
import <vector>; // 将 vector.h 转为 header unit(实验性)
import <iostream>; // 大多数编译器不支持
// 当前(2024)最佳实践:
// - 标准库仍用 #include
// - 第三方库用 #include
// - 自定义模块用 import
math.cppm
export module math;
export namespace math {
int add(int a, int b) { return a + b; }
double PI = 3.14159;
class Calculator { /* ... */ };
}
// 使用
import math;
int main() {
math::Calculator calc;
auto pi = math::PI;
}
logger.cppm
export module logger;
export namespace logger {
enum class Level { Debug, Info, Warning, Error };
class Logger {
public:
void log(Level level, const std::string& msg);
};
}
namespace logger {
// 内部实现(不导出)
void write_to_file(const std::string& msg);
std::string format_message(Level level, const std::string& msg);
}
global.cppm
module;
export module global;
export int global_value = 42;
// 使用
import global;
int main() {
int x = global_value; // 直接访问
}
shape.cppm
export module shape;
export class Point {
public:
Point(double x, double y) : x_(x), y_(y) {}
double distance_to(const Point& other) const {
double dx = x_ - other.x_;
double dy = y_ - other.y_;
return std::sqrt(dx * dx + dy * dy);
}
private:
double x_, y_;
};
export class Circle {
public:
Circle(Point center, double radius)
: center_(center), radius_(radius) {}
double area() const { return 3.14159 * radius_ * radius_; }
private:
Point center_;
double radius_;
};
main.cpp
import shape;
int main() {
Point p1(0, 0);
Point p2(3, 4);
Circle c(p1, 5);
std::cout << p1.distance_to(p2) << "\n"; // 5
std::cout << c.area() << "\n"; // ~78.54
}
特性模块类头文件类
编译速度快(只编译一次)慢(每次都解析)
名称查找无宏污染可能被宏干扰
虚函数表正常正常
模板实例化需要显式自动
导出控制export 关键字class 前加 export
mymodule.h
#ifndef MYMODULE_H
#define MYMODULE_H
// 如果编译器支持,优先使用模块
#ifdef __cpp_modules
import mymodule; // 模块接口
#else
#include "mymodule.hpp" // 回退到头文件
#endif
#endif
// mymodule.hpp
// 传统头文件实现(当模块不可用时)
class MyClass { /* ... */ };
// C++20 允许导入某些头文件作为 header units
// 大多数编译器支持有限
// 当前最佳实践(2024)
#include <spdlog/spdlog.h> // 第三方库用头文件
#include <nlohmann/json.hpp> // header-only 库
import myapp.core; // 自定义模块用 import
int main() {
spdlog::info("Hello"); // 正常工作
}
// wrapper.h - 桥接头文件
#pragma once
// 包含所有需要的头文件
#include <vector>
#include <string>
#include <memory>
// 定义所有导出
class MyClass { /* ... */ };
void helper_function();
// mymodule.cppm - 模块接口
module;
export module mymodule;
// 重新导出需要的内容
export import std;
export import wrapper;
编译器模块支持备注
GCC11+基本支持,实验性
Clang15+基本支持,实验性
MSVC19.29+基本支持,较完善
ICC2021.10+部分支持
NVCC不支持GPU 代码不支持
// 1. std 模块不支持
import std; // 失败
import std.core; // 失败
// 2. import <header> 有限支持
import <vector>; // 可能失败,取决于编译器
// 3. 模块分区可能不完全
import mymodule:detail; // 某些编译器可能不支持
// 4. CMake 支持不完善
// 需要设置 CMAKE_CXX_MODULE_FLAG 或使用实验性选项
Terminal window
# GCC 14
g++ -std=c++20 -fmodules-ts -xc++-module mymodule.cppm
g++ -std=c++20 -fmodules-ts main.cpp mymodule.gcm
# Clang 17
clang++ -std=c++20 -fmodules main.cpp
# MSVC
cl /std:c++20 /exportHeader main.cpp
/project
├── src/
│ ├── main.cpp
│ ├── math/
│ │ ├── math.cppm # 模块接口
│ │ └── math.cpp # 模块实现
│ ├── utils/
│ │ ├── utils.cppm
│ │ └── utils.cpp
│ └── lib/
│ ├── lib.cppm
│ └── lib.cpp
├── include/ # 对外 API(如果需要头文件兼容)
│ ├── mylib.h
│ └── mylib_export.h
└── build/
cmake_minimum_required(VERSION 3.25)
project(MyProject CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 模块支持(实验性)
option(ENABLE_MODULES "Enable C++20 modules" OFF)
if(ENABLE_MODULES)
target_compile_options(${PROJECT_NAME} PRIVATE
$<$<CXX_COMPILER_ID:GNU>:-fmodules-ts>
$<$<CXX_COMPILER_ID:Clang>:-fmodules>
)
endif()
# 源文件
add_executable(myapp
src/main.cpp
src/math/math.cpp
src/utils/utils.cpp
)
target_include_directories(myapp PRIVATE include)
utils.h
// ===== 阶段1:头文件 =====
#pragma once
#include <string>
class Utils {
public:
static std::string format(const std::string& s);
};
// ===== 阶段2:混合(模块 + 头文件)=====
// utils.cppm
module;
export module utils;
export import std; // 未来
#include <string> // 当前
// 保留原有接口
export class Utils {
public:
static std::string format(const std::string& s);
};
// ===== 阶段3:完全模块化 =====
// utils.cppm
module;
export module utils;
export import std;
export class Utils {
public:
static std::string format(const std::string& s);
};
// ✅ 使用模块组织大型库
export module mylib;
// ✅ 明确导出需要公开的接口
export class PublicClass { };
class InternalClass { }; // 不导出,内部使用
// ✅ 模块分区用于大型项目
export module mylib:core; // 核心接口
export module mylib:extra; // 扩展功能
// ✅ 混用头文件和模块(当前)
#include <spdlog/spdlog.h> // 第三方
import mymodule; // 自定义
// ✅ 使用 .cppm 扩展名区分模块接口文件
// mylib.cppm - 模块接口
// mylib.cpp - 模块实现
// ❌ 不要导出实现细节
export class InternalHelper { }; // 不要
// ❌ 不要在模块内使用 #define 宏(会污染)
#define MAX_SIZE 100 // 避免
// ❌ 不要混用模块分区和命名空间
export module foo:bar; // 分区,不是命名空间
namespace foo { // 这是命名空间
module bar; // 错误
}
// ❌ 不要假设 std 模块可用
import std; // 当前会失败
// 推荐:简短、有意义的模块名
export module math;
export module logger;
export module network;
// 避免:过长或过于具体
export module com::company::project::module::submodule;
// 分区命名
export module network:http; // 网络的 HTTP 部分
export module network:tcp; // 网络的 TCP 部分
export module json:parser; // JSON 的解析部分

import 语法速查:

语法说明
export module M;声明并导出模块 M
module M;声明内部模块(非导出)
import M;导入模块 M
export import M;重新导出模块 M
module M:N;模块 M 的 N 分区
import M:N;导入模块 M 的 N 分区
module;模块接口文件的开始

模块 vs 头文件:

方面模块头文件
编译速度快慢
宏隔离完全可能污染
重复编译无有
标准库支持否(未来)是
第三方库有限完整

当前建议:

  • 大型项目用模块加速编译
  • 标准库仍用 #include
  • 第三方库用 #include
  • 混用是安全的
  • 等待 import std; 标准化(C++26)

继续学习:查看 ch28 了解更多 C++20 现代特性。