C++模块是否会导致ODR违规?

Már*_*ldi 5 c++ one-definition-rule c++-modules

N4720 C++模块草案中,[basic.def.odr]/6说:

[......] 对于拥有出口申报单的实体,该实体只应有一个定义; 仅当模块的抽象语义图包含实体的定义时才需要诊断.[注意:如果定义不在接口单元中,则最多一个模块单元可以拥有并使用该定义. - 尾注] [...]

根据我的理解,实际上模板只有一次机会被编译器解析一次,与当前事态形成对比(每个翻译单元的定义都有一个精确的副本).这对于具有类似情况的其他实体也是有效的,例如内联函数/变量.

我的问题来自这样一个事实:由于每个翻译单元最多只能有一个实体定义(如[basic.def.odr]/1中所述),因此在TU中对实体进行不同的定义是未定义的行为. .并且,由于导出的实体在整个编译单元中只有一个定义(使未实现的定义对于它们的实现单元是唯一的),从我的角度来看,定义错误即使不是不可能也是更难的.

最后,简单地说:将(或确实或应该)模块的使用使得不可能违反ODR规则,或者更难以出错?

Nic*_*las 5

如果一个项目是完全模块化的(即从不使用#include),那么大多数意外的 ODR 违规都会消失。大多数意外的 ODR 违规是由于以下性质而发生的#include:包括具有定义的全局变量等。或者两阶段模板查找问题,其中两个文件包含相同的模板,但由于每个文件在该模板之前包含的内容,两个模板的定义是不同的。

但是,这并不能防止较少的“意外”ODR 违规。因为“同一个实体”是由其名称定义的,而一个实体的名称与其导出的模块无关,所以两个模块可以提供同一实体的不同定义。所以基本上,名称冲突。

如果单个翻译单元导入这两个模块,这只会成为编译错误(即:需要诊断)。如果两个独立的翻译单元每个都包含一个具有不同定义的模块,那么整个程序仍然违反 ODR,但不必对其进行诊断。

因此,即使在完全模块化的代码库中,仍然可能违反 ODR。

  • [P0142R0](https://wg21.link/p0142r0) 比standardese-d 的最终提案可读性更强,在4.5 中明确提到没有模块命名空间:*它是格式错误的(但没有诊断需要),用于包含两个模块的程序,这些模块从相同的命名空间*导出具有相同名称的相同种类/类型的两个实体。那里提到,这可以通过模块命名空间来解决,但没有这样做,因为*我们认为链接的概念不应该被提升到它所属的地方*,即实现源代码之上。 (3认同)