为什么匿名命名空间不被标准委员会视为命名空间静态的充分替代?

64
根据这个答案,在C++11中取消了对命名空间范围的静态变量的弃用。也就是说,在C++03中它们被弃用了,因为匿名命名空间被认为更好。但是C++11取消了对它们的弃用。
为什么呢?N3296列出了其中的原因
引用: “在命名空间范围内使用static不应该被弃用。匿名命名空间不能完全替代这个功能。”
显然,委员会接受了这个观点。为什么呢?匿名命名空间有什么不足以完全替代这个功能的地方吗?
我希望得到一些有关标准委员会讨论的文档或记录的答案。

1
不是真正的文本记录,但从链接问题的副本中可以看到,有来自2010年11月标准委员会会议的注释,表明该功能永远不会被从语言中删除。 - André Caron
相关链接:https://dev59.com/32445IYBdhLWcg3wucio - MvG
3个回答

40

这是一个更深入的解释。

尽管 7.3.1.1 [namespace.unnamed] 已经说明,在命名空间作用域中使用 static 关键字声明变量已经被弃用,因为匿名命名空间提供了更好的替代方案,但考虑到与 C 兼容性的问题,这个特性在可预见的未来不太可能被删除。委员会应该考虑取消废弃标记。

我知道的一个问题是,匿名命名空间无法在命名空间块外专门化模板。这就是为什么引入了 inline namespace,尽管也可以使用 static。此外,static 在宏中表现更好。


1
+1 针对模板特化和内联命名空间的解释。 - emsr
1
基于 static 的模板特化将违反 ODR,因为每个模板 ID 的解析(给定完整的模板参数列表)需要独立于实例化点,包括不同翻译单元中的点。 - Potatoswatter
1
关于专门化模板是什么意思?无论如何,专门化与主模板的链接都是独立的。(如果实现知道它们不能在其他地方命名,因为它们涉及其他本地符号,它们可能会在实践中使用本地符号来实现。) - Davis Herring

35

使用未命名的命名空间,你无法在当前命名空间内为变量提供内部链接。但是使用static可以做到。例如,下面使用未命名命名空间的示例不会为全局变量提供内部链接。

namespace { int a; } 
int a; // oops, no error!

如果第一个a被声明为static,那么尝试在全局范围内声明第二个a的尝试将立即导致错误,因为第一个a已经存在于全局范围内。

因此,为了实现它们使标识符唯一的目标,未命名的命名空间将实体放置到不同的命名空间中(除了影响它们的链接之外)。static只影响链接,而不改变函数和变量所属的命名空间。


1
你不能通过使用 using <this-namespace>::a; 声明来防止这种情况发生吗? - Potatoswatter
1
我尝试编译那个程序:unnam.cpp: 在函数 'int main()' 中: unnam.cpp:11: 错误:对 'a' 的引用不明确。 - Erik Aronesty
@ErikAronesty 我怀疑你并没有尝试过这个,因为我的代码中没有 main 函数,而你的编译器却抱怨了一个名为 main 的函数内的一些代码。所以如果你使用上述代码,并且想知道为什么它在更广泛的上下文中引发了错误(我相信有一个确定的解释),请提出一个新的 Stackoverflow 问题。 - Johannes Schaub - litb
作用域与链接无关。 - user2394284
3
@user2394284,我猜我不确定你的评论意味着什么,或者它应该澄清什么。链接的定义是这样的...“当一个名称可能表示与在另一个范围中引入声明的名称相同的内容时,就说它具有/链接/”。如果一个定义需要另一个定义,那么断言它们互不相关似乎是一个很大的牵强附会。 - Johannes Schaub - litb
虽然你所说的是正确的,但在我看来,为了实现static声明,你可以使用inline匿名命名空间。尽管你的示例可以编译,但任何尝试使用a变量都会导致不可避免的歧义错误。 (如果未将匿名命名空间声明为“inline”,则情况并非完全如此,我意识到问题没有提到“inline”命名空间)。 - davmac

16
用户答案描述为,未命名命名空间中的名称(标准术语为匿名命名空间)具有外部链接性,而在命名空间级别声明为static的名称具有内部链接性。
内部链接性有两个好处,其中一个也是未命名命名空间提供的好处:
1. 它们使名称对翻译单元本地化。我可以在不违反一个定义规则的情况下在不同的翻译单元中定义相同的函数fun。这种属性由未命名命名空间中的名称共享,通过为它们添加唯一的命名空间名称。
2. 它们防止名称进入全局符号表。这仅仅是一种优化,但在实践中非常重要。这种属性不被未命名命名空间中的名称所共享。
因此,通常情况下,将其翻译成使用static来生成仅在翻译单元中有效的namespace级函数的程序会为连接器生成更少的工作并且执行速度比使用未命名命名空间的等效程序更快。
尽管如此,您需要使用未命名命名空间来传递作为模板参数的类型,因为模板参数必须具有外部链接性。
因此,我通常会这样做:将自由函数定义为static,但将类型放入未命名命名空间中。

2
感谢您的解释和分享您的惯常做法!我认为这可以被视为“最佳实践”之一。 - Siu Ching Pong -Asuka Kenji-
5
我知道cppreference.com不是标准,但我从未见过他们犯错。在http://en.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces上,他们说未命名的命名空间具有内部链接。所以:内部还是外部? - davidbak
2
代码如何执行得更快?你是不是只是指链接速度更快? - paulm
3
除了用语与标准不符外,这是一个很好的答案。具体而言,标准规定未命名命名空间中的名称具有“内部链接”。我认为你想表达的观点是,static 函数的名称通常甚至不会被添加到连接器所需的符号表中。然而,未命名命名空间中的函数通常会被添加到符号表中,但标记为内部或混淆,因此它们实际上是无法访问的。 - Adrian McCarthy
3
未命名命名空间中的函数名称与“static”函数的处理方式相同,均具有内部链接,因此它们不需要以与“static”函数不同的方式添加到符号表中(至少GCC是这样处理的)。我认为这个答案是不正确的。 - davmac
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接