未命名/匿名名称空间与静态函数
C ++的一个功能是能够创建未命名(匿名)名称空间的功能,如下所示:
namespace { int cannotAccessOutsideThisFile() { ... } } // namespace
我们会认为这样的功能将是无用的-因为我们无法指定名称空间的名称,所以无法从外部访问名称空间中的任何内容。但是,这些未命名的名称空间可在创建它们的文件中访问,就好像我们对它们具有隐式的使用子句一样。
我的问题是,为什么或者何时比使用静态函数更好?还是它们本质上是做同一件事的两种方式?
解决方案
C ++标准的内容在第7.3.1.1节"未命名的名称空间"的第2段中:
The use of the static keyword is deprecated when declaring objects in a namespace scope, the unnamed-namespace provides a superior alternative.
静态仅适用于对象,函数和匿名联合的名称,不适用于类型声明。
编辑:
不赞成使用static关键字(影响翻译单元中变量声明的可见性)的决定已被撤销(参考)。在这种情况下,使用静态或者未命名的名称空间实际上又回到了做完全相同的事情的两种方式。有关更多讨论,请参见此SO问题。
未命名的名称空间仍然具有允许我们定义本地翻译单元类型的优点。请参阅此SO问题以获取更多详细信息。
感谢Mike Percy引起我的注意。
我只是在阅读问题时才了解此功能,所以只能推测一下。与文件级静态变量相比,这似乎提供了多个优点:
- 匿名名称空间可以彼此嵌套,从而提供了多个保护级别,使符号无法逃脱。
- 可以在同一个源文件中放置几个匿名名称空间,实际上在同一个文件中创建了不同的静态级别作用域。
我想了解是否有人在真实代码中使用了匿名名称空间。
为此,C ++ 98标准不建议使用static关键字。静态的问题在于它不适用于类型定义。它也是一个重载的关键字,在不同的上下文中以不同的方式使用,因此未命名的名称空间会简化一些事情。
将方法放置在匿名名称空间中可防止我们意外违反"一个定义规则",从而使我们不必担心将辅助方法命名为与我们可能链接的其他方法相同。
而且,正如luke所指出的那样,该标准比静态成员更喜欢匿名名称空间。
我最近开始在代码中用匿名名称空间替换静态关键字,但立即遇到一个问题,即名称空间中的变量不再可在调试器中检查。我使用的是VC60,所以我不知道其他调试器是否有问题。我的解决方法是定义一个"模块"命名空间,在该命名空间中为其命名为cpp文件的名称。
例如,在我的XmlUtil.cpp文件中,我为我所有的模块变量和函数定义了一个命名空间XmlUtil_I {...}。这样,我可以在调试器中应用XmlUtil_I ::限定条件来访问变量。在这种情况下," _ I"将其与可能要在其他地方使用的公共名称空间(例如XmlUtil)区分开。
我认为,与真正匿名的方法相比,这种方法的潜在缺点是,有人可以通过在其他模块中使用名称空间限定符来违反所需的静态范围。我不知道这是否是一个主要问题。
在一种极端情况下,静电会产生令人惊讶的影响(至少对我而言)。 C ++ 03标准在14.6.4.2/1中规定:
For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2) except that: For the part of the lookup using unqualified name lookup (3.4.1), only function declarations with external linkage from the template definition context are found. For the part of the lookup using associated namespaces (3.4.2), only function declarations with external linkage found in either the template definition context or the template instantiation context are found. ...
下面的代码将调用foo(void *)
而不是我们可能期望的foo(S const&)
。
template <typename T> int b1 (T const & t) { foo(t); } namespace NS { namespace { struct S { public: operator void * () const; }; void foo (void*); static void foo (S const &); // Not considered 14.6.4.2(b1) } } void b2() { NS::S s; b1 (s); }
就其本身而言,这可能没什么大不了的,但是它确实突出表明,对于完全兼容的C ++编译器(即支持" export"的编译器)," static"关键字仍将具有无法以其他任何方式使用的功能。 。
// bar.h export template <typename T> int b1 (T const & t); // bar.cc #include "bar.h" template <typename T> int b1 (T const & t) { foo(t); } // foo.cc #include "bar.h" namespace NS { namespace { struct S { }; void foo (S const & s); // Will be found by different TU 'bar.cc' } } void b2() { NS::S s; b1 (s); }
确保使用ADL在模板中找不到我们未命名的名称空间中的函数的唯一方法是将其设置为"静态"。
现代C ++更新
从C ++ '11开始,未命名名称空间的成员隐式具有内部链接(3.5 / 4):
An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage.
但与此同时,更新了14.6.4.2/1,以消除对链接的提及(摘自C ++ '14):
For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2) except that: For the part of the lookup using unqualified name lookup (3.4.1), only function declarations from the template definition context are found. For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context or the template instantiation context are found.
结果是静态名称空间成员和未命名名称空间成员之间的特定区别不再存在。
根据经验,我只会注意到,虽然这是将以前静态的函数放入匿名名称空间的C ++方法,但较早的编译器有时可能会遇到问题。我目前正在为目标平台使用一些编译器,而更现代的Linux编译器可以将函数放入匿名名称空间中。
但是在Solaris上运行的较早版本的编译器(直到未指定的将来发行版),有时会被接受,而有时将其标记为错误。错误不是让我担心的原因,而是接受它时可能正在做的事情。因此,在全面实现现代化之前,我们仍将使用静态(通常是类作用域)的函数,而我们更希望使用匿名名称空间。