为什么没有放弃 C++17 的单一定义规则?



引用C++草案N4713:

每个程序都应包含每个非内联的一个定义odr在程序中使用的函数或变量废弃语句(9.4.1);无需诊断。定义可以显式出现在程序中,也可以在标准中找到或用户定义的库,或者(在适当的时候)它是隐式的定义的(见15.1、15.4和15.8)。内联函数或变量应在使用odr的每个翻译单元中定义在丢弃的语句之外。

在C++17之前的C++版本中,我只需声明我的函数inline就可以绕过这个限制。C++17为变量添加了相同的特性。

此外,在我看来,inline-关键字除了可以忽略ODR之外,没有其他用途。

那么,为什么不放弃C++17的整个规则呢?我看不出可以关闭的规则的目的。

inline"关闭"ODR不是免费的:inline实体的定义必须存在于每个翻译单元中。请注意,这意味着对其定义的任何更改都会导致使用它的每个编译单元的重新编译。当函数是许多/大型项目所依赖的库的一部分时,这将特别令人不快。

另一方面,非inline函数只存在于一个编译单元中,当其他地方需要时,链接器会通过一些符号引用它们。遵守ODR可以保证符号不含糊。

inline既危险又昂贵。

它很昂贵,因为现在每个使用某个东西的编译单元都取决于该东西的定义。所以改变身体?重新编译它的每个用户。

这是危险的,因为如果两个inline定义不一致,那么您的程序就是if-NDR(格式错误,不需要诊断)。

如果没有inline,两个定义会导致程序格式错误,但编译器必须提供诊断;通常是一个严重的错误。

inline关闭这个非常有用的警告。

如果每个编译器都能够将不同inline定义的If-NDR转换为诊断错误消息,那么就会有更多的情况。只要证明这很困难和/或没有实现,inline就是一个"激活不安全模式!"选项。让它违约会适得其反。

权衡的是,无论在哪里使用inline函数,都需要定义它。如果您想要,只需将整个程序放在一个.cpp文件中即可。

ODR是您需要进行单独编译的地方,这仍然很有用。

来自cppreference:

每个非内联函数或变量的唯一定义使用的odr(见下文)需要出现在整个程序(包括任何标准库和用户定义库)。这个编译器不需要诊断此冲突,但需要诊断行为违反它的程序的是未定义的。

将函数声明为inline不会"忽略"ODR,但它会导致函数的每个外观都是它自己的实体,需要在使用它的每个翻译单元中进行定义。微小但显著的差异。ODR仍然需要有单独的翻译单元。

最新更新