C++ 中的单一定义规则到底说了什么? 我能找到的唯一值得信赖的地方是《C++ 编程语言,第 3 篇》。编辑,第 9.2.3 页。除此之外,该规则还有任何官方定义吗?
翻译单元不得包含更多内容 比任何变量的一个定义, 函数、类类型、枚举类型 或模板。[...]
每个程序都应准确包含 每个非内联的一个定义 函数或对象
用于 那个程序;无需诊断。 定义可以明确地出现 在程序中,可以找到 标准或用户定义的库,或 (在适当的时候)它是隐式的 定义(见 12.1、12.4 和 12.8)。一个 内联函数应定义在 它所在的每个翻译单元 使用过。
[basic.def.odr] 子条款。 本子条款包含许多规则。
总而言之,它指出您不能多次定义某事物,或者如果您这样做,定义必须相同。它还说,如果你使用某种东西,就必须存在一个定义。以下部分更详细地描述了这一点。每个翻译单元只有一个定义
[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 使用的所有内容
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”的每个函数或变量的定义;无需诊断。
这个词最初只是叫“使用”; C++11 引入了术语“odr-use”以使其更加正式。