在头文件中使用未命名名称空间将如何导致ODR冲突? 在谷歌C++风格指南中,该节指出“在头文件中使用未命名命名空间可以容易地导致C++定义规则(ODR)的违反。”

在头文件中使用未命名名称空间将如何导致ODR冲突? 在谷歌C++风格指南中,该节指出“在头文件中使用未命名命名空间可以容易地导致C++定义规则(ODR)的违反。”,c++,one-definition-rule,unnamed-namespace,C++,One Definition Rule,Unnamed Namespace,我理解为什么在实现文件中不使用未命名的名称空间会导致ODR冲突,但不理解在头文件中如何使用。这怎么会导致违规?在test.h中 namespace { int x; } namespace{ int x; } 在任何源文件中包含该头文件都会导致ODR冲突,因为x定义了两次。这是因为编译器为未命名命名空间提供了唯一标识符,并且翻译单元中所有出现的未命名命名空间都提供了相同的标识符。换言之:每个TU最多有一个未命名的名称空间。原因是如果您实际使用匿名名称空间中的任何内容 命名空间,则可能

我理解为什么在实现文件中不使用未命名的名称空间会导致ODR冲突,但不理解在头文件中如何使用。这怎么会导致违规?

在test.h中

namespace {
  int x;
}

namespace{
  int x;
}

在任何源文件中包含该头文件都会导致ODR冲突,因为
x
定义了两次。这是因为编译器为未命名命名空间提供了唯一标识符,并且翻译单元中所有出现的未命名命名空间都提供了相同的标识符。换言之:每个TU最多有一个未命名的名称空间。

原因是如果您实际使用匿名名称空间中的任何内容 命名空间,则可能会出现未定义的行为。例如:

namespace {
double const pi = 3.14159;
}

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

当然,即使没有匿名名称空间,这也会 此处未定义的行为(因为
const
表示通过 默认),但基本原则成立。标题中的任何用法 未命名命名空间(或中定义的任何常量对象)中的任何内容 标题)可能会导致未定义的行为。是否 这是不是一个真正的问题取决于,但肯定是任何 真正涉及到上面的
pi
地址,将导致 问题。(我在这里说“真的”,因为有很多案例 正式使用地址或参考,但在 实际上,内联扩展将产生实际值 正在使用。当然,令牌
3.14159
3.14159

不管它出现在哪里。

他们的风格指南还包含其他的“令人信服的”建议,以避免r引用引用、例外、lambdas、STD::Fuff/Band,可能更多的是他们知道为什么……谷歌C++风格指南并不完全是因为特别有用或好的建议而出名……我同意你们两个的观点。但这不是我要问的。亲密的选民愿意提供一个理由吗?他们提供的理由是这个问题“主要是基于意见的”。这类问题通常都是这样,但我强烈不同意投票人的看法。我认为这个问题不是真的。你问的问题可以很客观地回答,从标题中可以很清楚地看出这一点。我认为这是一个好问题。@Plasmah很难想象今天有人试图使用右值引用、lambda或
std::function/bind
编写可移植代码。(不久前,关于模板也可以这么说;我不知道是否仍然是这样,但我最后一次查看时,Mozilla指南禁止了它们,只有少数特权人士知道如何解决不同编译器中的差异——即使在今天,编写不同的模板代码也很容易感谢Rakibul-我没有意识到多个未命名的名称空间在一个TU中被赋予相同的唯一名称。这太愚蠢了。删除匿名名称空间,你就会遇到完全相同的问题。这是我最初的想法,James,然而,我遗漏的一点信息可能正是未命名名称空间被认为更具风险的原因:如果大多数人不知道同一TU中的多个未命名名称空间块实际上是同一个未命名名称空间,那么他们可能不会本能地看到Rakibul示例中的ODR冲突,虽然我没有,但这仍然与问题无关。具有相同名称的多个命名空间是相同的命名空间,句点
std::vector
std::list
位于同一命名空间中,尽管它们位于完全不同的头文件中,并且几乎可以肯定位于单独的
namespace std{…}
块中。@boycy和真正的问题,当然是未发现的违反ODR的情况。上面需要一个编译器诊断。所有这些都不能真正回答这个问题,它只是描述了一个未命名命名空间中的名称如何在每个TU中生成该对象的单独实例,这与ODR无关。正如您的代码所示,将const对象放在头中的未命名命名空间中没有意义,但这不是我的问题。Rakibul@boycy完美地回答了未命名名称空间如何导致ODR冲突的问题,但它确实会导致未定义的行为。请参见示例:函数
twoPiR
具有未定义的行为。这当然是谷歌编码指南中规则的动机(Rakibul的答案与未命名的名称空间无关)。你的回答很有趣,肯定让我更深刻地理解了其中的细微差别,以及为什么谷歌可能制定了这条规则,所以谢谢你。但这并没有回答我的问题,这与谷歌的理论基础无关,而是与ODR冲突有关。ODR冲突在于
twoPiR
有两个不同的定义(ODR说,如果两个定义相同,则内联函数只能定义多次)。定义是不同的,因为一个使用
file1::pi
,另一个使用
file2::pi
@boycy。我的解释是为什么在头中禁止使用未命名名称空间的常见原因。我不能为谷歌说话,但这是这条规则的理由(自从名称空间诞生以来,我在每一个编码准则中都看到了这条规则)