假设我们有定义模块 foo 的模块接口源文件 foo.ixx。我们用
import foo;
在许多不同的 cpp 文件中。与传统头文件 foo.h 包含在许多不同的 cpp 文件中的情况相比,编译时间会减少吗?如果编译时间减少了,为什么会这样呢?
假设我们有定义模块 foo 的模块接口源文件 foo.ixx。我们用
import foo;
在许多不同的 cpp 文件中。与传统头文件 foo.h 包含在许多不同的 cpp 文件中的情况相比,编译时间会减少吗?如果编译时间减少了,为什么会这样呢?
是的,模块的优点之一是它可以减少编译时间。为了比较,这是今天的做法:
// foo.hpp
// some code
// a.cpp
#include "foo.hpp"
// b.cpp
#include "foo.hpp"
现在,当 2 个翻译单元a.cpp
和b.cpp
被编译时,some code
文本包含在这些源文件中,因此some code
被编译两次。虽然链接器会注意最终可执行文件中实际上只有一个定义,但编译器仍然需要编译some code
两次,这是浪费精力。
使用模块,我们将有类似的东西:
// foo.hpp
export module foo;
// some code
// a.cpp
import foo;
// b.cpp
import foo;
现在编译过程不同了;有一个中间阶段,foo.hpp
编译成可以被a.cpp
, 和使用的格式,b.cpp
也就是说实现文件不需要编译some code
,some code
直接使用里面的定义即可。
这意味着foo.hpp
只需要编译一次,这可能会导致编译时间的大幅减少,尤其是在消耗模块接口单元的实现文件数量增加的情况下。
“从实现文件访问头文件的机制是使用 C 预处理器中的 include 指令。换句话说,您的头文件被隐式复制了很多次。
所有头文件的许多副本分散在一个项目中,编译器必须一遍又一遍地传递和解析它们。最明显的问题之一是代码编译时间。
模块有效地替换了头文件和预处理器包含指令。模块提出的解决方案建议我们摆脱 C 预处理器的文本包含,因此,它的所有缺点。” [每个模块只处理一次。见表 2]
标头包含机制依赖于文本预处理(本质上类似于解释脚本语言);由于程序员的错误,这既耗时又难以追踪错误。另一方面,模块导入机制是一种更智能的机制,用于分离接口和实现,更好地保证一致性和正确性。它提供了在同一个翻译单元中定义接口和实现的方法,从而可以匹配两者并通知 lib 开发人员在传统包含系统中可能不会注意到的错误。因此,不仅构建时间,而且整个开发周期都显着缩短。