【问题标题】:How would use of unnamed namespaces in headers cause ODR-violations?在标头中使用未命名的命名空间会如何导致 ODR 违规?
【发布时间】:2014-05-14 10:33:24
【问题描述】:

在 Google C++ 样式指南中,Namespaces 部分指出“在头文件中使用未命名的命名空间很容易导致违反 C++ 单一定义规则 (ODR)。

我明白为什么在实现文件中使用未命名的命名空间会导致 ODR 违规,但不知道在标头中使用如何做到这一点。这怎么会导致违规?

【问题讨论】:

  • 他们的风格指南还包含其他“有趣”的建议,以避免右值引用、异常、lambdas、std::function/bind 以及可能只有他们知道原因的更多...
  • Google C++ 风格指南并不以特别有用或好的建议而闻名...
  • 我同意你们俩的观点,但这不是我要问的。亲密的选民愿意提供理由吗?
  • 他们提供的原因是这个问题“主要基于意见”。对于这种 type 的问题,这通常是正确的,但我强烈不同意亲密的选民。我不认为 this 问题是真的。您在问一些可以很客观地回答的问题,这从标题中很清楚。我认为这是一个很好的问题。
  • @PlasmaHH 今天很难想象有人尝试使用右值引用、lambda 或std::function/bind 编写可移植代码。 (不久前,模板也可以这样说;我不知道现在是否仍然如此,但我上次查看时,Mozilla 指南禁止它们,除非在少数知情的特权人士手中究竟如何解决各种编译器之间的差异——即使在今天,在 VS 和 g++ 中编写具有不同语义的模板代码也很容易。)

标签: c++ one-definition-rule unnamed-namespace


【解决方案1】:

原因是如果你真的在匿名中使用了任何东西 命名空间,您冒着未定义行为的风险。例如:

namespace {
double const pi = 3.14159;
}

inline double twoPiR( double r ) { return 2.0 * pi * r; }

内联函数(以及类、模板和 必须在多个翻译中定义的任何其他内容 单位)是令牌必须相同(通常情况下, 除非你打了一些宏),并且所有符号都必须绑定 同样。在这种情况下,每个翻译单元都有一个单独的 pi 的实例,因此 twoPiR 中的 pi 绑定到不同的 每个翻译单元中的实体。 (有几个例外, 但它们都涉及积分表达式。​​)

当然,即使没有匿名命名空间,这也是 此处未定义的行为(因为const 表示内部链接由 默认),但基本原则成立。在标题中的任何使用 未命名命名空间中的任何内容(或定义在 标头)可能会导致未定义的行为。无论 是不是一个真正的问题取决于,但肯定是任何 确实涉及到pi的地址,上面,会导致 问题。 (我这里说“真的”,因为有很多情况 地址或引用被正式使用的地方,但在 实践中,内联扩展实际上会导致值 正在使用。当然,令牌3.141593.14159 不管它出现在哪里。)

【讨论】:

  • James 这些都没有真正回答这个问题,它只是描述了一个未命名的命名空间中的名称如何导致每个 TU 的该对象的单独实例,这与 ODR 无关。正如您的代码所举例说明的那样,将 const 对象放在标头中的未命名命名空间中没有意义,但这不是我的问题。 Rakibul 完美地回答了未命名的命名空间如何导致 ODR 违规的问题。
  • @boycy 除了它确实会导致未定义的行为。请参阅示例:函数 twoPiR 具有未定义的行为。这当然是 Google 编码指南中规则的动机(而 Rakibul 的回答与未命名的命名空间无关)。
  • 啊。您的回答很有趣,并且肯定让我对这其中的细微差别以及 Google 可能制定规则的原因有了更深入的了解,所以谢谢您。但它并没有回答我的问题,这与 Google 的基本原理无关,而是与 ODR 违规有关。
  • ODR 违规是twoPiR 有两个不同的定义(ODR 表示如果两个定义相同,内联函数只能定义多次)。定义不同,因为一种使用file1::pi,另一种使用file2::pi
  • @boycy 我的解释是标题中禁止使用未命名命名空间的常见原因。我不能代表 Google,但这是我使用的编码指南中的规则(自从引入命名空间以来我看到的所有编码指南中都有)的理由。
【解决方案2】:

在test.h中

namespace {
  int x;
}

namespace{
  int x;
}

在任何源文件中包含该头文件都会违反 ODR,因为x 被定义了两次。发生这种情况是因为编译器为未命名的命名空间提供了唯一标识符,并且翻译单元中所有出现的未命名命名空间都被赋予了相同的标识符。套用一句话:每个 TU 最多有一个未命名的命名空间。

【讨论】:

  • 感谢 Rakibul - 我没有意识到多个未命名的命名空间在一个 TU 中具有相同的唯一名称。
  • 这很愚蠢。删除匿名命名空间,您将遇到完全相同的问题。
  • 这是我最初的想法,James,但是我遗漏的一些信息可能是未命名命名空间被认为在这里更具风险的确切原因:如果大多数人不知道多个未命名的命名空间同一个 TU 中的命名空间块实际上是 same 未命名的命名空间,那么他们可能不会本能地看到 Rakibul 示例中的 ODR 违规,因为我没有。
  • 但这仍然与问题无关。多个同名命名空间是同一个命名空间,句号。 std::vectorstd::list 在同一个命名空间中,尽管它们位于完全不同的头文件中,而且几乎可以肯定是在单独的 namespace std {...} 块中。
  • @boycy 当然,真正的问题是未发现的违反 ODR 的案例。以上需要编译器诊断。
猜你喜欢
  • 1970-01-01
  • 2015-08-01
  • 2011-11-29
  • 2012-05-31
  • 2016-09-13
  • 2012-03-26
  • 1970-01-01
  • 1970-01-01
  • 2014-05-09
相关资源
最近更新 更多