C++20中两种类型的模块文件(接口和实现)有什么意义?
What is the point of two types of module files (interface and implementation) in C++20?
当我想导出我写的东西时 export void foo();
我可以在同一个模块文件中实现它,也可以在一个单独的文件中实现它。但是,正式区分这些文件(export module mymodule
与 module mymodule
)有什么意义呢,无论如何,我可以拥有任意数量的后一种类型。将 export
关键字放在我想要制作的东西之前 public 并且不需要为特殊的接口文件而烦恼是不够的吗?
模块实现单元可以在它们的模块接口中使用带有 module linkage(既不是 export
也不是 static
)的东西。如果所有模块单元都是接口和实现单元,则需要某种机制来处理这种访问的循环性。
这个实现方式是在编译的模块接口文件中包含module-linkage实体:有助于实现立即是否翻译单元需要这种处理。 (这类似于需要知道文件属于哪个模块才能正确地破坏其符号名称。)
此外,要求预先声明模块接口的所有部分(在主接口或它招募的接口分区中)避免需要“link” 将多个独立模块单元的接口结果合并为一个CMI,供导入器消费。模块实现单元不能影响导入器,除非通过 linking 在不同的符号定义中,这一事实也有利于构建系统:导入器不需要 重新编译 当模块实现单元而不是接口单元发生变化。
在某些时候,构建系统发现某些文件说 import MyModule;
。当它看到时,构建系统需要去寻找 MyModule
.
的模块
如果MyModule
还没有构建,构建系统需要构建它。为此,它必须(除其他外)扫描项目中所有已知的源文件,以查看哪些用于构建 MyModule
。但最重要的是它需要弄清楚它具体需要构建哪个文件才能让 import MyModule
现在工作。
如果系统只需要查找要构建的单个文件(这样,系统可以使用快速扫描器预处理所有内容以查找所有这些文件),则该过程效果最佳且最快。所以模块系统规定:对于任何特定的模块,都有一个主要的模块接口,它定义了模块导出的一切。构建该模块可能会引发其他模块的编译,但我们知道在 import MyModule
可以工作之前必须完成哪个文件的构建。
现在坚持一切一个模块可以导出到一个文件中并不是最好的主意。所以在很多情况下,您会有多个导出内容的文件,并且您将 export import
它们放在主 MyModule
主界面文件中。但是由于模块名称是全局的,我们不希望许多微小的模块名称弄乱命名空间。
输入模块分区:这些是模块接口文件,其名称在特定模块中命名空间。模块接口文件可以包括其他分区,但仅限于同一模块内的分区。显然,分区包含图必须是无环的。
但这给我们留下了一个小问题。假设您有一个定义 class 的分区,该分区被导出到主模块接口。但是您不想将那些成员函数的实现放在那个分区文件中。那么...它去了哪里?
我的意思是,您可以将它放在另一个不被任何人导入的分区中。但是如果那个分区没有被导入……为什么还要给它一个分区名呢?最好能立即传达这个“分区”不能。
输入模块实现单元。它们是特定模块的一部分,因此它们可以导入该模块的 partitions。但是它们不能自己被任何人导入。
这就是他们的目的。
但请注意,构建系统知道它不需要构建模块实现单元来完全构建模块。它只需要构建主模块接口文件及其包含的任何分区(直接或间接)。如果将实现放入实现单元,这允许模块重建尽可能快。
最后,模块实现单元(和接口单元)可以访问它们从分区不导出 导入的任何名称。这些模块本地名称只能在模块内访问。
当我想导出我写的东西时 export void foo();
我可以在同一个模块文件中实现它,也可以在一个单独的文件中实现它。但是,正式区分这些文件(export module mymodule
与 module mymodule
)有什么意义呢,无论如何,我可以拥有任意数量的后一种类型。将 export
关键字放在我想要制作的东西之前 public 并且不需要为特殊的接口文件而烦恼是不够的吗?
模块实现单元可以在它们的模块接口中使用带有 module linkage(既不是 export
也不是 static
)的东西。如果所有模块单元都是接口和实现单元,则需要某种机制来处理这种访问的循环性。
这个实现方式是在编译的模块接口文件中包含module-linkage实体:有助于实现立即是否翻译单元需要这种处理。 (这类似于需要知道文件属于哪个模块才能正确地破坏其符号名称。)
此外,要求预先声明模块接口的所有部分(在主接口或它招募的接口分区中)避免需要“link” 将多个独立模块单元的接口结果合并为一个CMI,供导入器消费。模块实现单元不能影响导入器,除非通过 linking 在不同的符号定义中,这一事实也有利于构建系统:导入器不需要 重新编译 当模块实现单元而不是接口单元发生变化。
在某些时候,构建系统发现某些文件说 import MyModule;
。当它看到时,构建系统需要去寻找 MyModule
.
如果MyModule
还没有构建,构建系统需要构建它。为此,它必须(除其他外)扫描项目中所有已知的源文件,以查看哪些用于构建 MyModule
。但最重要的是它需要弄清楚它具体需要构建哪个文件才能让 import MyModule
现在工作。
如果系统只需要查找要构建的单个文件(这样,系统可以使用快速扫描器预处理所有内容以查找所有这些文件),则该过程效果最佳且最快。所以模块系统规定:对于任何特定的模块,都有一个主要的模块接口,它定义了模块导出的一切。构建该模块可能会引发其他模块的编译,但我们知道在 import MyModule
可以工作之前必须完成哪个文件的构建。
现在坚持一切一个模块可以导出到一个文件中并不是最好的主意。所以在很多情况下,您会有多个导出内容的文件,并且您将 export import
它们放在主 MyModule
主界面文件中。但是由于模块名称是全局的,我们不希望许多微小的模块名称弄乱命名空间。
输入模块分区:这些是模块接口文件,其名称在特定模块中命名空间。模块接口文件可以包括其他分区,但仅限于同一模块内的分区。显然,分区包含图必须是无环的。
但这给我们留下了一个小问题。假设您有一个定义 class 的分区,该分区被导出到主模块接口。但是您不想将那些成员函数的实现放在那个分区文件中。那么...它去了哪里?
我的意思是,您可以将它放在另一个不被任何人导入的分区中。但是如果那个分区没有被导入……为什么还要给它一个分区名呢?最好能立即传达这个“分区”不能。
输入模块实现单元。它们是特定模块的一部分,因此它们可以导入该模块的 partitions。但是它们不能自己被任何人导入。
这就是他们的目的。
但请注意,构建系统知道它不需要构建模块实现单元来完全构建模块。它只需要构建主模块接口文件及其包含的任何分区(直接或间接)。如果将实现放入实现单元,这允许模块重建尽可能快。
最后,模块实现单元(和接口单元)可以访问它们从分区不导出 导入的任何名称。这些模块本地名称只能在模块内访问。