7

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

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

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

我的问题源于这样一个事实,因为每个翻译单元最多只能有一个实体定义(如[basic.def.odr]/1中所述),因此跨 TU 具有不同的实体定义是未定义的行为. 而且,由于导出的实体在整个编译单元中只有一个定义(使未导出的实体对其实现单元唯一),因此在我看来,如果不是不可能的话,弄错定义会更难。

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

4

1 回答 1

6

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

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

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

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

于 2018-02-07T15:47:36.420 回答