最佳答案
目前,C++ 实现实际上只有两个与代码对应的“东西”:我们人工编写和编辑的源代码,以及编译器根据源代码输出的汇编。
因为 C++ 模板是“具体化”的,所以每个模板实例化都会产生单独的程序集。因此,不能在定义模板的地方生成任何程序集,而只能在使用它们的地方生成。这就是为什么模板必须在头文件中,以便它们基本上可以复制粘贴到使用点(这就是#include 的全部内容)。
这个想法是有代码的第三种表示。想象一下,编译器在内部有某种内部表示在它已经解析了代码但是在它开始产生汇编。它产生的“事物”最终是抽象语法树(AST)的某种表示。它基本上就是你的程序,从对人类最简单的形式映射到对计算机最简单的形式。
这是非常粗略的模块背后的想法(或者至少是它们的实现)。你拿出你的代码,然后吐出某种代表 AST 的文件。此 AST 是您程序的完整表示,因此它是完全无损的。它知道你声明的模板的一切,等等。当一个模块被加载时,它只会加载这个文件,编译器可以完全使用它,就好像它拥有所有可用的源一样。但是,将人类可读的源代码转换为这个 AST 的步骤实际上是一个相当昂贵的步骤。从 AST 开始会快很多。
如果您只有一个翻译单元,这会比较慢。毕竟解析->codegen还是比解析->序列化->反序列化->codegen快。但是假设你有 10 个翻译单元,它们都是 #include vector 。您将解析 vector 中的代码 10 次。此时,序列化/反序列化的额外成本被您只需解析一次的事实所抵消(并且反序列化可以比解析快得多;这种数据格式将专门设计用于使反序列化快速,而源代码是设计为可读、向后兼容等)。
预编译的头文件在某种意义上是模块的预览:https://clang.llvm.org/docs/PCHInternals.html
关于c++ - C++ 模块系统中如何处理模板?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43861385/