一个定义规则 - 编译

One Definition Rule - compilation

我正在尝试了解 ODR。 我创建了一个这样的文件 pr1.cpp:

struct S{
  int a;
};

第二个文件 pr2.cpp 像这样

struct S {
  char  a;
};

和这样的主文件:

#include <iostream>

int main() { 
  return 0;
}

我正在使用终端和命令进行编译:

g++ -Wall -Wextra pr1.cpp pr2.cpp main.cpp -o mypr

编译器没有发现任何类型的错误,但是有两个 "S" 类型的声明...我不明白到底发生了什么...我认为在 "linkage" 阶段因为 ODR 违规.. 我只能在编辑 main.cpp 文件时遇到错误,添加:

#include "pr1.cpp"
#include "pr2.cpp"

任何人都可以向我解释发生了什么事吗?

除非您在同一个文件中包含这两个定义,否则没有问题。这是因为编译器在通常是 .cpp 文件的单个翻译单元上运行。您在此文件中 #include 的所有内容也是翻译单元的一部分,因为预处理器基本上复制并粘贴所有包含文件的内容。

编译器会为每个翻译单元创建目标文件(通常是 .obj),然后 linker 会通过 [=28= 创建一个可执行文件(或 .dll 等) ]ing 项目依赖的所有目标文件和库。在您的情况下,编译器在不同的翻译单元中遇到了每个结构,因此它看不到问题。当您包含这两个文件时,这两个定义现在会发现它们位于同一个翻译单元中,并且编译器会抛出错误,因为如果在该翻译单元中使用了 S(即使您没有为程序格式错误使用一个)。

作为旁注,不要在其他 .cpp 文件中包含 .cpp 文件。我相信您可以找到很多关于如何在头文件和源文件中组织代码的信息,但它并没有直接回答问题,所以我不会展开讨论。

编辑:我忘了说为什么你没有收到 linker 错误。一些评论指出,这是未定义的行为,这意味着即使您的 linker 可能应该抱怨,但实际上不必抱怨。在您的情况下,每个结构都有一个 .obj 文件和一个 main.obj。 None 这些参考另一个所以 linker 看不到任何需要解析的参考,并且它可能不会费心检查不明确的符号。

我假设如果您声明 struct S; 并尝试使用 S*S&(实际 S需要在同一翻译单元内定义)。那是因为 linker 需要解析那个符号并且它会找到两个匹配的定义。但是,鉴于这是未定义的,符合标准的 linker 可以只选择一个并默默地 link 将您的程序变成无意义的东西,因为您打算使用另一个。这对于从一个 .cpp 传递到另一个 .cpp 的结构尤其危险,因为定义需要保持一致。当同名 structs/classes 通过库边界传递时,这也可能是一个问题。由于这些原因,请始终避免重复名称。