什么是单一定义规则?

问题描述 投票:0回答:2

C++ 中的单一定义规则到底说了什么? 我能找到的唯一值得信赖的地方是《C++ 编程语言,第 3 篇》。编辑,第 9.2.3 页。除此之外,该规则还有任何官方定义吗?

c++ language-lawyer definition one-definition-rule
2个回答
39
投票
真相就在标准中(3.2 一个定义规则):

翻译单元不得包含更多内容 比任何变量的一个定义, 函数、类类型、枚举类型 或模板。

[...]

每个程序都应准确包含 每个非内联的一个定义 函数或对象

用于 那个程序;无需诊断。 定义可以明确地出现 在程序中,可以找到 标准或用户定义的库,或 (在适当的时候)它是隐式的 定义(见 12.1、12.4 和 12.8)。一个 内联函数应定义在 它所在的每个翻译单元 使用过。


0
投票
“单一定义规则”是 C++ 标准中的

[basic.def.odr] 子条款。 本子条款包含许多规则。

总而言之,它指出您不能多次定义某事物,或者如果您这样做,定义必须相同。它还说,如果你使用某种东西,就必须存在一个定义。以下部分更详细地描述了这一点。

每个翻译单元只有一个定义

[1]

第一个也是最基本的规则是

[basic.def.odr] p2:

任何

翻译单元不得包含任何可定义项的多个定义。

例如,这意味着您不能编写以下内容:

void foo() {} // OK void foo() {} // error, more than one definition of foo in the same TU
实际上,编译器会抛出一个错误,告诉您“重新定义 foo”。


[1] 翻译单元基本上是一个 C++ 源文件。标头不是翻译单元,它包含在翻译单元中。

每个程序只有一个定义,不包括模板和

inline

此外,

[basic.def.odr] p14 调节多个翻译单元之间发生的情况:

对于任何可定义的项目

D

 具有多个翻译单元的定义,

    if
  • D
     是非内联非模板化函数或变量,或者 
  • 如果不同翻译单位的定义不满足以下要求,
程序格式不正确; [...]

例如,这意味着您不能在不同的源文件中有两个定义

void foo() {}

。实际上,这会导致链接器错误告诉您“foo 的多个定义”。

模板和内联函数很特殊,因为它们可以在多个翻译单元中定义,只要这些定义遵循严格的规则。本质上,定义必须是等效的。例如:

// a.cpp inline int bar() { return 0; } // b.cpp inline int bar() { return 0; } // OK // c.cpp inline int bar() { return 1; } // ill-formed, no diagnostic required
请注意,此规则对于标头非常重要。 

#include

本质上是将代码复制/粘贴到翻译单元中,因此在标头中定义任何内容都有违反此规则的风险。

odr 使用的所有内容

[2] 都必须定义

第三,它还定义了所谓的“odr-use”。直观上,

odr-use 意味着您以某种构造的方式使用其定义需要存在的方式。 例如: int foo(); // declaration, not a definition using foo_type = decltype(foo); // decltype (unevaluated operand) is not odr-use. int x = foo(); // calling a function is odr-use

对于
odr-use
某些事物会产生后果,如

[basic.def.odr] p11: 中所述 每个程序应至少包含该程序中“已废弃的语句”之外的“odr-used”的每个函数或变量的定义;无需诊断。

在实践中,如果您使用一个函数并且它没有在任何地方定义,您会收到一个链接器错误,告诉您“没有定义 foo”。 [2]

这个词最初只是叫“使用”; C++11 引入了术语“odr-use”以使其更加正式。


© www.soinside.com 2019 - 2024. All rights reserved.