C++中的“一定义规则”到底是什么意思?
我能找到的唯一可靠的出处是《C++程序设计语言,第三版,第9.2.3节》。除此之外,还有其他官方对该规则的定义吗?
真相在标准中(3.2 一个定义规则):
任何翻译单元中都不应存在多个变量、函数、类类型、枚举类型或模板的定义。
[...]
每个程序必须包含每个非内联函数或对象的确切定义,该函数或对象在该程序中被使用;无需诊断。定义可以明确地出现在程序中,在标准或用户定义的库中找到,或者(适当时)是隐式定义的(见12.1、12.4和12.8)。内联函数必须在使用它的每个翻译单元中定义。
第一条也是最基本的规则是[basic.def.odr] p2:
例如,这意味着你不能写出以下内容:
void foo() {} // OK
void foo() {} // error, more than one definition of foo in the same TU
[1] 一个翻译单元基本上是一个C++源文件。头文件不是一个翻译单元,它被包含在翻译单元中。
inline
此外,[basic.def.odr] p14规定了在多个翻译单元之间发生的情况:
对于任何在多个翻译单元中具有定义的可定义项
D
,
- 如果
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-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-used的每个函数或变量,除非该函数或变量在discarded statement之外;无需进行诊断。
实际上,如果你使用一个函数,但在任何地方都没有定义它,你会得到一个链接错误,告诉你“没有定义foo”。