为什么 struct 和 class 定义可以在多个翻译单元上重复?
Why struct and class definitions can repeat over multiple translation unit?
根据一个定义规则,class和结构定义不能在一个翻译单元中重复。
但是,如果它们实际上是定义,那么为什么允许它们在多个翻译单元中重复出现。为什么在那种情况下链接器不抛出多重定义错误?
例如- 以下应该按照相同的逻辑抛出多重定义错误
test.h
#ifndef TEST
#define TEST
class s {
int a;
int b;
};
#endif
test1.cpp
#include "test.h"
int main() {}
test2.cpp
#include "test.h"
Class定义包含广义的两部分——成员变量定义和成员函数declarations/defintions。 (我意识到 类 可能包含嵌套类型、枚举、typedef 等)
成员变量定义提供了创建对象的模板,它们不是可执行代码。因此,将它们定义在多个文件中应该不是问题。
成员函数声明就是 -- 声明。再一次,它们不是可执行代码。因此,将它们定义在多个文件中应该不是问题。
成员函数定义,如果它们是内联的,将被视为与任何其他内联函数一样。在多个文件中定义它们应该不是问题。
成员函数定义,如果它们不是内联的,如果它们定义在.h文件中并且.h文件在多个翻译单元中是#include
d,将会导致问题。
But then why is the case that they are allowed to be repeated in multiple translation units, if they are in reality definitions.
在语言层面,答案很简单:因为标准是这么说的,特别是在[basic.def.odr]/6
There can be more than one definition of a class type, enumeration type, inline function with external linkage ([dcl.inline]), inline variable with external linkage ([dcl.inline]), class template, non-static function template, static data member of a class template, member function of a class template, or template specialization for which some template parameters are not specified ([temp.spec], [temp.class.spec]) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. […]
当然,语言规则之所以如此,是有原因的。在实现级别,class 的定义只是告诉编译器在相应 class 类型的对象上运行的代码必须如何处理业务,例如,对象成员位于何处等等.但是 class 定义本身并不能真正生成代码。编译器有必要在使用 class 的每个翻译单元中看到每个 class 类型的定义,以便它可以独立地为每个翻译单元生成正确的代码。 class 类型的定义在每个翻译单元中也必须相同,以便为每个翻译单元生成的代码与为所有其他翻译单元生成的代码兼容。
Why doesnt the linker throw a multiple definition error in that case ?
在符号级别工作的链接器最终只能看到生成的目标代码。并且 class 定义在机器代码级别没有明确的表示。 class 的概念在那个级别上并不存在。它们存在于代码运行的方式中,而不是直接存在于代码中。如果你愿意,他们就住在装配线之间……
C 语言旨在用于具有非常原始链接器的平台,对它们的要求最低。 C++ 语言要求更多。对于实际支持链接用 C++ 编写的单独编译模块的平台,其链接器必须至少支持弱符号和部分符号或其他类似结构。如果弱符号存在两个或多个弱定义,并且没有强定义,则链接器会将弱符号之一(任意选择)视为强符号并忽略其余符号。如果一个符号存在两个或多个部分定义,则相关数据将以通常任意的顺序连接起来,并且对该符号的所有引用都将标识连接的 blob 的开始。通常,还会有一种生成符号的方法,该符号具有标识 blob 的长度或结束地址的相关名称。
一种要求链接器具有此类功能的语言可以为程序员提供比即使是极简主义链接器也必须可用的语言更好的语义。
根据一个定义规则,class和结构定义不能在一个翻译单元中重复。
但是,如果它们实际上是定义,那么为什么允许它们在多个翻译单元中重复出现。为什么在那种情况下链接器不抛出多重定义错误?
例如- 以下应该按照相同的逻辑抛出多重定义错误
test.h
#ifndef TEST
#define TEST
class s {
int a;
int b;
};
#endif
test1.cpp
#include "test.h"
int main() {}
test2.cpp
#include "test.h"
Class定义包含广义的两部分——成员变量定义和成员函数declarations/defintions。 (我意识到 类 可能包含嵌套类型、枚举、typedef 等)
成员变量定义提供了创建对象的模板,它们不是可执行代码。因此,将它们定义在多个文件中应该不是问题。
成员函数声明就是 -- 声明。再一次,它们不是可执行代码。因此,将它们定义在多个文件中应该不是问题。
成员函数定义,如果它们是内联的,将被视为与任何其他内联函数一样。在多个文件中定义它们应该不是问题。
成员函数定义,如果它们不是内联的,如果它们定义在.h文件中并且.h文件在多个翻译单元中是#include
d,将会导致问题。
But then why is the case that they are allowed to be repeated in multiple translation units, if they are in reality definitions.
在语言层面,答案很简单:因为标准是这么说的,特别是在[basic.def.odr]/6
There can be more than one definition of a class type, enumeration type, inline function with external linkage ([dcl.inline]), inline variable with external linkage ([dcl.inline]), class template, non-static function template, static data member of a class template, member function of a class template, or template specialization for which some template parameters are not specified ([temp.spec], [temp.class.spec]) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. […]
当然,语言规则之所以如此,是有原因的。在实现级别,class 的定义只是告诉编译器在相应 class 类型的对象上运行的代码必须如何处理业务,例如,对象成员位于何处等等.但是 class 定义本身并不能真正生成代码。编译器有必要在使用 class 的每个翻译单元中看到每个 class 类型的定义,以便它可以独立地为每个翻译单元生成正确的代码。 class 类型的定义在每个翻译单元中也必须相同,以便为每个翻译单元生成的代码与为所有其他翻译单元生成的代码兼容。
Why doesnt the linker throw a multiple definition error in that case ?
在符号级别工作的链接器最终只能看到生成的目标代码。并且 class 定义在机器代码级别没有明确的表示。 class 的概念在那个级别上并不存在。它们存在于代码运行的方式中,而不是直接存在于代码中。如果你愿意,他们就住在装配线之间……
C 语言旨在用于具有非常原始链接器的平台,对它们的要求最低。 C++ 语言要求更多。对于实际支持链接用 C++ 编写的单独编译模块的平台,其链接器必须至少支持弱符号和部分符号或其他类似结构。如果弱符号存在两个或多个弱定义,并且没有强定义,则链接器会将弱符号之一(任意选择)视为强符号并忽略其余符号。如果一个符号存在两个或多个部分定义,则相关数据将以通常任意的顺序连接起来,并且对该符号的所有引用都将标识连接的 blob 的开始。通常,还会有一种生成符号的方法,该符号具有标识 blob 的长度或结束地址的相关名称。
一种要求链接器具有此类功能的语言可以为程序员提供比即使是极简主义链接器也必须可用的语言更好的语义。