为何模块分区无法间接导入主模块接口单元所导入的模块?
复现代码
MA.cpp
export module MA; import MB; export int MyFunA() { return 0; }
MB.cpp
export module MB; export int MyFuncB() { return 0; }
MA-PA.cpp
module MA:PA; import MA; int x = MyFuncB(); // #1 此处报错
main.cpp
import MA; int main() { return 0; }
CMakeLists.txt
project(ModuleTestProject) cmake_minimum_required(VERSION 3.5) add_executable(the_executable MB.cpp MA.cpp MA-PA.cpp main.cpp)
编译错误
使用GCC 12.2编译(命令:g++ -std=c++20 -fmodules-ts)时,报错:
MA-PA.cpp:5:9: error: 'MyFuncB' was not declared in this scope 5 | int x = MyFuncB(); | ^~~~~~~
使用Visual Studio 2022 17.4.1编译时,报类似错误:
C3861 'MyFuncB': identifier not found ModuleTestProject
删除#1行后,两款编译器均可编译成功。
疑问与标准依据
在模块分区MA:PA中调用MB的MyFuncB时,编译器提示未找到该标识符。查阅C++标准§10.3/7:
Additionally, when a module-import-declaration in a module unit of some module M imports another module unit U of M, it also imports all translation units imported by non-exported module-import-declarations in the module unit purview of U.These rules can in turn lead to the importation of yet more translation units.
认为MA:PA应该能间接导入MB的理由:
MA:PA导入了主模块接口单元MA,而MA导入了MBMA.cpp和MA-PA.cpp同属module MA的翻译单元
此前了解到主模块接口单元可以间接导入模块分区导入的模块,符合上述标准条款,但编译器行为却显示模块分区导入主模块接口单元时,无法间接获取主模块导入的模块,希望明确理解误区。
解答
你的理解误区在于对模块单元导入传递方向和标准条款适用场景的误读:
标准条款的核心逻辑
该条款描述的是:当模块M的某个单元导入M的另一个单元U时,会同时导入U的范围内未导出的import声明所引入的翻译单元。但关键问题是:主模块接口单元的import MB不会自动传递给导入它的模块分区。MA.cpp中的import MB是主模块接口单元的内部依赖,仅服务于自身的实现或内部逻辑,不属于MA模块对外暴露的接口部分,因此不会被导入MA的模块分区继承。
模块内部导入的单向传递性
- 主模块接口单元导入模块分区时,会继承分区的非导出导入——这就是你之前看到的“主模块能间接导入分区导入的模块”的情况,但这个传递是单向的:分区无法反向继承主模块的导入依赖。
正确解决方式
如果模块分区MA:PA需要使用MB的内容,必须显式导入MB:module MA:PA; import MA; import MB; // 显式导入依赖 int x = MyFuncB();
内容的提问来源于stack exchange,提问作者YZ_SO

