【问题标题】:How to apply ODR(One Definition Rule) in c++?如何在 C++ 中应用 ODR(一个定义规则)?
【发布时间】:2017-04-22 15:14:37
【问题描述】:

我阅读了Definitions and ODR 页面,还阅读了一些关于 ODR 的相关问题。仍然不明白为什么它很重要。

例如在file1.cpp中:

int val = 1;

在file2.cpp中:

int val = 2;

在 main.cpp 中:

extern int val;

这种情况违反了 ODR。但在实际情况下,有人会这样做吗?我的意思是对具有相同名称的相同类型的变量的定义似乎很自然。即使有人这样做,编译器或链接器也不会同意。我不明白我们为什么要关心 ODR,因为如果我们编写了错误的代码,编译器或链接器就会报错。是否有可能在不被发现的情况下违反 ODR?谁能列出一些我们经常犯的真实案例或错误?

【问题讨论】:

  • 编译器可能根本不会给您任何错误。 odr-violation 会导致未定义的行为,因此您的程序可以做任何事情。
  • "这些代码无法编译" - 是的,它们可以。但是,它们不应该是可链接的。
  • @NeilButterworth 在这种情况下可能是正确的,但在某些非常相似的情况下,它甚至可以毫无问题地链接。不要相信链接器会检测所有 ODR 违规。
  • @hvd 这就是为什么我说“不应该”,而不是“不会”。
  • @NeilButterworth 我已经更正了我的描述。感谢您的建议。

标签: c++


【解决方案1】:

我不明白我们为什么要关心 ODR,因为如果我们写错了代码,编译器就会报错。

ODR 是唯一允许编译器发出错误消息的东西。如果我们没有 ODR,编译器就必须接受这样的代码。

在编译代码时是否可能违反 ODR?

当然。根据编译器的不同,这可能编译和链接没有任何问题,或者可能会失败:

extern int var;
int main() {
  int myvar = var;
}

请注意这里缺少var 的定义。

一些编译器看到局部变量myvar 完全未使用,因此也删除了对var 的引用。其他编译器/链接器会抱怨缺少var 的定义。而对于其他一些人,这取决于您传递的编译器标志。

另一个例子:

// t1.cc
int var;
// t2.cc
long var;

一些编译器给int varlong var 赋予相同的内部名称并检测冲突,其他编译器包含类型并且看不到任何冲突。

【讨论】:

  • 检测这些问题取决于链接器,而不是编译器。除了当前正在编译的翻译单元之外,编译器无法看到翻译单元中的值。
  • @NeilButterworth 是编译器为链接器提供了足够的信息来检测错误。在我的第一个示例中,是编译器优化了对var 的引用,这不是链接器在做的。并且 ODR 还涵盖了int var; int var;,这通常由编译器而不是链接器检测到。无论如何,我认为编译器和链接器之间的区别不再相关,现在编译器正在实现 LTO,从而接管了传统上链接器工作的一部分。
  • 对我来说,您似乎对这两个有点困惑 - 例如,我不明白为什么您认为您的第一个代码示例可能在编译时失败。
  • @NeilButterworth “您认为您的第一个代码示例可能在编译时失败”?那不是我写的。
  • " 这可能编译和链接没有任何问题,或者它可能会失败:" - 我会添加“在链接时”。恕我直言,您的回答确实混淆了读者的编译和链接时间。
【解决方案2】:

修复机箱小记

您可以在翻译单元或匿名命名空间中声明static 等变量。

static 全局变量确实具有内部链接,因此不会违反 ODR。

file1.cpp

 static int val = 1;
 // OR
 namespace
 {
   int val = 1;
 }

file2.cpp

static int val = 2;
// OR
namespace
{
  int val = 2;
}

现实生活中的潜在案例

如果您有 2 个 DLL 或 SO,它们都具有相同的全局变量,并且您在应用程序中加载了两个共享库,该怎么办?

【讨论】:

  • 我想知道我们将如何犯编译器或链接器无法检测到的错误。
猜你喜欢
  • 1970-01-01
  • 2021-12-08
  • 1970-01-01
  • 2020-04-03
  • 1970-01-01
  • 2016-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多