当我在 A.cpp
中有以下代码时和 B.cpp
没有生成警告或错误,但是 Initializer::Initializer()
在B.cpp
在 A.cpp
中被调用两次不会被调用。
static int x = 0;
struct Initializer
{
Initializer()
{
x = 10;
}
};
static Initializer init;
由于这违反了单一定义规则并导致未定义的行为,我认为这是完全正常的。但是,当我将构造函数定义移到一个或两个文件中的类声明之外时,如下所示:
static int x = 0;
struct Initializer
{
Initializer();
};
Initializer::Initializer()
{
x = 10;
}
static Initializer init;
链接器突然变得足够聪明,可以出错并说出 one or more multiply defined symbols found
.这里发生了什么变化,为什么重要?我本以为链接器应该始终能够检测到 ODR 损坏 - 什么情况下它不能检测到?
如果我错了,请有人纠正我,但我的理论是,当您使用模板化代码(定义总是在标题中)时,您最终会在许多编译单元中得到重复的定义。它们碰巧都是相同的,因此链接器是否只选择一个并孤立其他的并不重要,但它不会出错,因为有多个定义或模板不起作用。
最佳答案
您的第二个示例明显且易于诊断是否违反了单一定义规则。它有两个带有外部链接的非内联函数的定义。这对于链接器来说很容易诊断,因为从它所链接的目标文件中包含的函数的名称来看,违规是显而易见的。
您的第一个示例以更微妙的方式打破了单一定义规则。因为在类主体中定义的函数被隐式声明为内联
,所以您必须检查函数主体以确定是否违反了一个定义规则。
仅出于这个原因,我对您的实现未能发现违规行为(我第一次没有发现)并不感到惊讶。显然,当单独查看一个源文件时,编译器不可能发现违规,但检测违规和链接时间的信息可能实际上并不存在传递给链接器的目标文件。它肯定超出了我期望链接器找到的范围。
关于c++ - 链接器何时会在多个定义的符号上出错?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11095187/