一定义规则是什么?

52
C++中的“一定义规则”到底是什么意思? 我能找到的唯一可靠的出处是《C++程序设计语言,第三版,第9.2.3节》。除此之外,还有其他官方对该规则的定义吗?

强相关:重新定义标准库中的函数是否违反了一个定义规则? - iammilind
2个回答

39

真相在标准中(3.2 一个定义规则):

任何翻译单元中都不应存在多个变量、函数、类类型、枚举类型或模板的定义。

[...]

每个程序必须包含每个非内联函数或对象的确切定义,该函数或对象在该程序中被使用;无需诊断。定义可以明确地出现在程序中,在标准或用户定义的库中找到,或者(适当时)是隐式定义的(见12.1、12.4和12.8)。内联函数必须在使用它的每个翻译单元中定义。


3
嗯,情况比那复杂一些。它还涉及到多个翻译单元和规则例外(这使得讨论变得有点困难!)。干杯! - Cheers and hth. - Alf
3
实际上,我本来要引用整页内容,但我只选择了必要的部分。 - icecrime

1
“一定义规则”是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

  • 如果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-used[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的每个函数或变量,除非该函数或变量在discarded statement之外;无需进行诊断。

实际上,如果你使用一个函数,但在任何地方都没有定义它,你会得到一个链接错误,告诉你“没有定义foo”。


这个术语最初只被称为“use”;C++11引入了“odr-use”这个术语,使其更加正式。

感谢您对“每个程序一个定义”条款和“odr-use”进行澄清。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接