提问人:sharptooth 提问时间:11/16/2010 最后编辑:Jan Schultkesharptooth 更新时间:9/26/2023 访问量:15241
什么是单一定义规则?
What is the One-Definition Rule?
问:
C++ 中的单定义规则到底说了什么? 我能找到的唯一可靠的例子是《C++编程语言》,第3版,第9.2.3页。除此之外,该规则是否有任何官方定义?
答:
真相在标准(3.2 一个定义规则)中:
任何翻译单元都不得包含更多 比任何变量的一个定义, 函数、类类型、枚举类型 或模板。
[...]
每个程序都应包含确切的内容 每个非内联的一个定义 中使用的函数或对象 该计划;无需诊断。 定义可以显式显示 在程序中,可以在 标准库或用户定义的库,或者 (在适当的情况下)它是隐含的 定义(见 12.1、12.4 和 12.8)。一 内联函数应在 它所在的每个翻译单元 使用。
评论
“单一定义规则”是 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
- 如果不同翻译单元中的定义不满足以下要求,
程序格式不正确;[...]
例如,这意味着在不同的源文件中不能有两个定义。在实践中,这将导致链接器错误,告诉您“foo 的多个定义”。void 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 会产生后果,如 [basic.def.odr] p11 所述:
在实践中,如果你使用一个函数,并且它没有在任何地方定义,你会得到一个链接器错误,告诉你“没有foo的定义”。
[2]这个术语最初只是叫“使用”;C++11引入了术语“odr-use”,使其更加正式。
评论