我得到了一个我不明白的奇怪行为。所以我在两个不同的 cpp 文件中定义了两个具有相同名称的不同类。我知道这不会在翻译单元的编译过程中导致任何错误,因为它们彼此不了解。但是,当链接器将这些文件链接在一起时,它不应该引发一些错误吗?
您正在考虑一个定义规则。我从那里引用(粗体是我选择的重点,而不是原始文档的一部分(。
您的理解是正确的 - 在多个编译单元中定义相同的函数是非法的:
每个 odr 使用的非内联函数或变量(见下文(都需要出现一个且只有一个定义才能出现在整个程序(包括任何标准和用户定义的库(中。编译器不需要诊断此冲突,但未定义违反此冲突的程序的行为。
但是,对于类来说,情况并非如此,只要定义都相同,就可以多次定义(每个编译单元中最多定义一次(。如果它们相同,则可以安全地将该类的实例从一个编译单元传递到另一个编译单元,因为所有编译单元都具有兼容的相同定义,具有兼容的大小和内存布局。
任何一个翻译单元中只允许任何变量、函数、类类型、枚举类型、概念(自 C++20 起(或模板的一个定义(其中一些可能有多个声明,但只允许一个定义(。
。
只要每个定义出现在不同的翻译单元中,程序中就可以有多个以下定义:类类型、枚举类型、具有外部链接的内联函数 带外部链接的内联变量(自 C++17 起(、类模板、非静态函数模板、类模板的静态数据成员、类模板的成员函数、 部分模板专用化,概念,(自 C++20 起(,只要满足以下所有条件:
- 每个定义由相同的标记序列组成(通常出现在同一个头文件中(
- 从每个定义中查找名称查找查找相同的实体(在重载解析之后(,除了具有内部链接或无链接的常量可以引用不同的对象,只要它们未使用 ODR 并且在每个定义中具有相同的值。
- 重载运算符(包括转换、分配和释放函数(引用每个定义中的相同函数(除非引用定义中定义的函数( 语言链接是相同的(例如,包含文件不在外部"C"块内(
- 上述三个规则适用于每个定义中使用的每个默认参数
- 如果定义是针对具有隐式声明构造函数的类,则使用 ODR 的每个转换单元都必须为基和成员调用相同的构造函数
- 如果定义是针对模板的,则所有这些要求在定义点同时适用于名称,在实例化时适用于从属名称
如果满足所有这些要求,则程序的行为就好像整个程序中只有一个定义一样。否则,行为是未定义的。
项目符号点是一种奇特且高度精确的方式,用于指定定义在文字和有效结果上必须相同。
一个定义规则特别允许这样做,只要这些定义是完全、纯粹的、相同的。
我的意思是绝对相同的。即使你交换令牌struct
为令牌class
,在无关紧要的情况下,你的程序有未定义的行为。
这是有充分理由的:通常我们在标头中定义类,并且通常将此类标头包含在多个翻译单元中;如果不允许这样做,那将是非常尴尬的。
出于同样的原因,这同样适用于inline
函数定义。
至于为什么你没有收到错误:嗯,就像我说的,未定义的行为。从技术上讲,工具链可以对此进行诊断,但是由于具有相同名称的多个类定义是完全司空见惯的事情(如上所述(,因此可以说是浪费时间,对于所有事物的链接器来说,尝试诊断"事故"是相当复杂的逻辑。最终,就像这种语言中的许多东西一样,它留给你来尝试把它做好。