为什么 C++20 模块导出的函数名会包含模块名?
数据小陈
结论
_ZW1A1ai 中包含模块名 A,是因为编译器采用了 strong module ownership:符号名会保留实体所属模块的信息,导出的函数也不例外。这是编译器实现中的 ABI 和名称修饰策略,C++ 标准并未规定所有编译器都必须使用这种编码格式。
问题中的例子是:
export module A;
export void a(int);
导出的函数可以在模块外使用,但这不代表链接器符号必须采用不含模块名的全局拼写。export 控制模块接口的可见性和可用性,并不要求编译器去掉实体的模块归属信息。
为什么导出函数仍带模块名
将模块名编码进符号,可以让 ABI 保留函数所属模块的信息。这样,即使不同模块中的实体在源代码里同名,链接时也能区分它们,从而减少名称冲突,并在编译和链接过程中保留模块归属信息。
strong module ownership 和 weak module ownership 的区别在于:
- strong module ownership:导出和非导出的相关实体都会在修饰名中体现模块归属。
- weak module ownership:通常只有非导出实体会体现模块归属,导出实体的修饰名不含模块名。
这两种方式都是实现选择。仅凭 C++ 源代码中的 export,无法判断所有编译器是否会生成相同的链接符号。
extern "C++" 的作用
extern "C++" 是一种语言链接说明。某些编译器允许用它影响模块实体的名称修饰。这说明导出函数带模块名并非因为函数不能用其他方式生成符号,但不代表标准要求编译器提供相同的开关,也不保证不同设置生成的 ABI 兼容。
extern "C++" {
// 编译器可能在此采用不同的名称修饰或归属规则
}
具体效果取决于编译器对模块和 ABI 的实现。如果要依赖这种写法,应查阅目标编译器的文档,并检查实际生成的符号。使用不同编译选项或不同工具链构建的代码,链接前尤其要确认兼容性。
注意事项
_ZW1A1ai 和 a@A(int) 是题目中 Linux 环境下的示例,不能视为跨平台、跨编译器或跨版本都固定不变的格式。名称修饰属于 ABI 细节。排查实际链接问题时,应检查当前工具链生成的符号,并确认参与链接的模块对象文件使用了兼容的编译器和选项。
备注:内容仅供参考。
