在头文件中使用未命名名称空间将如何导致ODR冲突? 在谷歌C++风格指南中,该节指出“在头文件中使用未命名命名空间可以容易地导致C++定义规则(ODR)的违反。”
我理解为什么在实现文件中不使用未命名的名称空间会导致ODR冲突,但不理解在头文件中如何使用。这怎么会导致违规?在test.h中在头文件中使用未命名名称空间将如何导致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最多有一个未命名的名称空间。原因是如果您实际使用匿名名称空间中的任何内容 命名空间,则可能
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。我的解释是为什么在头中禁止使用未命名名称空间的常见原因。我不能为谷歌说话,但这是这条规则的理由(自从名称空间诞生以来,我在每一个编码准则中都看到了这条规则)