为什么在多个 .cpp 文件中定义相同的 class 不会导致链接器多重定义错误?
Why does the same class being defined in multiple .cpp files not cause a linker multiple definition error?
我遇到了一个我无法理解的奇怪行为。因此,我在两个不同的 cpp 文件中定义了两个具有相同名称的不同 类。我知道这不会在翻译单元的编译过程中导致任何错误,因为它们彼此不了解。但是链接器在将这些文件链接在一起时不应该抛出一些错误吗?
您正在考虑 one definition rule。我从那里引用(粗体字是我选择的重点,不是原始文档的一部分)。
你的理解是正确的——在多个编译单元中定义同一个函数是非法的:
One and only one definition of every non-inline function or variable that is odr-used (see below) is required to appear in the entire program (including any standard and user-defined libraries). The compiler is not required to diagnose this violation, but the behavior of the program that violates it is undefined.
然而,classes 不是这种情况,它可以定义多次(每个编译单元最多一次),只要定义完全相同即可。如果它们相同,那么您可以安全地将 class 的实例从一个编译单元传递到另一个编译单元,因为所有编译单元都具有兼容的、相同的定义以及兼容的大小和内存布局。
Only one definition of any variable, function, class type, enumeration type, concept (since C++20) or template is allowed in any one translation unit (some of these may have multiple declarations, but only one definition is allowed).
...
There can be more than one definition in a program, as long as each definition appears in a different translation unit, of each of the following: class type, enumeration type, inline function with external linkage inline variable with external linkage (since C++17), class template, non-static function template, static data member of a class template, member function of a class template, partial template specialization, concept, (since C++20) as long as all of the following is true:
- each definition consists of the same sequence of tokens (typically, appears in the same header file)
- name lookup from within each definition finds the same entities (after overload-resolution), except that constants with internal or no linkage may refer to different objects as long as they are not ODR-used and have the same values in every definition.
- overloaded operators, including conversion, allocation, and deallocation functions refer to the same function from each definition (unless referring to one defined within the definition)
the language linkage is the same (e.g. the include file isn't inside an extern "C" block)
- the three rules above apply to every default argument used in each definition
- if the definition is for a class with an implicitly-declared constructor, every translation unit where it is odr-used must call the same constructor for the base and members
- if the definition is for a template, then all these requirements apply to both names at the point of definition and dependent names at the point of instantiation
If all these requirements are satisfied, the program behaves as if there is only one definition in the entire program. Otherwise, the behavior is undefined.
要点是一种奇特且高度精确的方式,用于指定定义在字母和有效结果中必须相同。
one-definition 规则明确允许这样做,只要这些定义完全、无误地相同。
我的意思是完全相同。即使您将令牌 struct
换成令牌 class
、in a case where it would otherwise not matter,您的程序也有未定义的行为。
这是有充分理由的:通常我们在 headers 中定义 classes,并且我们通常将这样的 headers 包含到多个翻译单元中;如果不允许的话就很尴尬了。
出于同样的原因,这同样适用于 inline
函数定义。
至于为什么你没有收到错误:好吧,就像我说的,未定义的行为。从技术上讲,工具链可以对此进行诊断,但是由于具有相同名称的多个 class 定义是一件非常普遍的事情(如上所述),因此想出什么是浪费时间可以说是浪费时间尝试诊断所有事物的链接器的逻辑非常复杂 "accidents"。最终,与这种语言中的许多事情一样,由您来尝试把它做好。
我遇到了一个我无法理解的奇怪行为。因此,我在两个不同的 cpp 文件中定义了两个具有相同名称的不同 类。我知道这不会在翻译单元的编译过程中导致任何错误,因为它们彼此不了解。但是链接器在将这些文件链接在一起时不应该抛出一些错误吗?
您正在考虑 one definition rule。我从那里引用(粗体字是我选择的重点,不是原始文档的一部分)。
你的理解是正确的——在多个编译单元中定义同一个函数是非法的:
One and only one definition of every non-inline function or variable that is odr-used (see below) is required to appear in the entire program (including any standard and user-defined libraries). The compiler is not required to diagnose this violation, but the behavior of the program that violates it is undefined.
然而,classes 不是这种情况,它可以定义多次(每个编译单元最多一次),只要定义完全相同即可。如果它们相同,那么您可以安全地将 class 的实例从一个编译单元传递到另一个编译单元,因为所有编译单元都具有兼容的、相同的定义以及兼容的大小和内存布局。
Only one definition of any variable, function, class type, enumeration type, concept (since C++20) or template is allowed in any one translation unit (some of these may have multiple declarations, but only one definition is allowed).
...
There can be more than one definition in a program, as long as each definition appears in a different translation unit, of each of the following: class type, enumeration type, inline function with external linkage inline variable with external linkage (since C++17), class template, non-static function template, static data member of a class template, member function of a class template, partial template specialization, concept, (since C++20) as long as all of the following is true:
- each definition consists of the same sequence of tokens (typically, appears in the same header file)
- name lookup from within each definition finds the same entities (after overload-resolution), except that constants with internal or no linkage may refer to different objects as long as they are not ODR-used and have the same values in every definition.
- overloaded operators, including conversion, allocation, and deallocation functions refer to the same function from each definition (unless referring to one defined within the definition) the language linkage is the same (e.g. the include file isn't inside an extern "C" block)
- the three rules above apply to every default argument used in each definition
- if the definition is for a class with an implicitly-declared constructor, every translation unit where it is odr-used must call the same constructor for the base and members
- if the definition is for a template, then all these requirements apply to both names at the point of definition and dependent names at the point of instantiation
If all these requirements are satisfied, the program behaves as if there is only one definition in the entire program. Otherwise, the behavior is undefined.
要点是一种奇特且高度精确的方式,用于指定定义在字母和有效结果中必须相同。
one-definition 规则明确允许这样做,只要这些定义完全、无误地相同。
我的意思是完全相同。即使您将令牌 struct
换成令牌 class
、in a case where it would otherwise not matter,您的程序也有未定义的行为。
这是有充分理由的:通常我们在 headers 中定义 classes,并且我们通常将这样的 headers 包含到多个翻译单元中;如果不允许的话就很尴尬了。
出于同样的原因,这同样适用于 inline
函数定义。
至于为什么你没有收到错误:好吧,就像我说的,未定义的行为。从技术上讲,工具链可以对此进行诊断,但是由于具有相同名称的多个 class 定义是一件非常普遍的事情(如上所述),因此想出什么是浪费时间可以说是浪费时间尝试诊断所有事物的链接器的逻辑非常复杂 "accidents"。最终,与这种语言中的许多事情一样,由您来尝试把它做好。