C++的一个特性是能够创建未命名(匿名)命名空间,如下所示:
namespace { int cannotAccessOutsideThisFile() { ... } } // namespace
您会认为这样的功能是无用的 - 因为您无法指定命名空间的名称,所以无法从外部访问其中的任何内容.但是这些未命名的命名空间可以在它们创建的文件中访问,就好像你有一个隐含的using子句.
我的问题是,为什么或何时使用静态函数会更好?或者他们基本上是两种做同样事情的方式?
C++标准在第7.3.1.1节"未命名的命名空间"中进行了介绍,第2段:
在声明命名空间作用域中的对象时,不推荐使用static关键字,unnamed-namespace提供了一个更好的替代方法.
静态仅适用于对象,函数和匿名联合的名称,而不适用于类型声明.
编辑:
弃用static关键字的这种使用(影响翻译单元中变量声明的可见性)的决定已被颠倒(ref).在这种情况下,使用静态或未命名的命名空间基本上是两种完全相同的方式.有关更多讨论,请参阅此 SO问题.
未命名的命名空间仍然具有允许您定义翻译单元本地类型的优点.有关更多详细信息,请参阅此 SO问题.
幸得迈克·珀西提出这个引起我的注意.
将方法放在匿名命名空间中可以防止您意外违反" 一个定义规则",从而使您永远不必担心命名助手方法与您可能链接的其他方法相同.
而且,正如luke所指出的,匿名命名空间是静态成员的标准首选.
有一个边缘情况静态具有惊人的效果(至少对我而言).C++ 03标准在14.6.4.2/1中声明:
对于依赖于模板参数的函数调用,如果函数名称是unqualified-id但不是template-id,则使用通常的查找规则(3.4.1,3.4.2)找到候选函数,除了:
对于使用非限定名称查找(3.4.1)的查找部分,仅找到具有来自模板定义上下文的外部链接的函数声明.
对于使用关联命名空间(3.4.2)的查找部分,仅找到在模板定义上下文或模板实例化上下文中找到的具有外部链接的函数声明.
...
以下代码将调用foo(void*)
而不是foo(S const &)
您所期望的.
templateint 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 templateint b1 (T const & t); // bar.cc #include "bar.h" template 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的模板中找不到未命名的命名空间中的函数的唯一方法就是实现它static
.
更新现代C++
从C++ '11开始,未命名的命名空间的成员隐式地具有内部链接(3.5/4):
未命名的命名空间或在未命名的命名空间中直接或间接声明的命名空间具有内部链接.
但与此同时,更新了14.6.4.2/1以删除链接的提及(这取自C++ '14):
对于postfix-expression是从属名称的函数调用,使用通常的查找规则(3.4.1,3.4.2)找到候选函数,除了:
对于使用非限定名称查找(3.4.1)的查找部分,仅找到模板定义上下文中的函数声明.
对于使用关联命名空间(3.4.2)的查找部分,仅找到在模板定义上下文或模板实例化上下文中找到的函数声明.
结果是静态和未命名的命名空间成员之间的这种特殊差异不再存在.
我最近开始在我的代码中用匿名命名空间替换静态关键字,但是立即遇到了一个问题,即命名空间中的变量不再可用于我的调试器中进行检查.我使用的是VC60,所以我不知道这是否与其他调试器没有问题.我的解决方法是定义一个"模块"命名空间,在那里我给它命名了我的cpp文件.
例如,在我的XmlUtil.cpp文件中,我为所有模块变量和函数定义了一个名称空间XmlUtil_I {...}.这样我就可以在调试器中应用XmlUtil_I :: qualified来访问变量.在这种情况下,'_ I'将它与我可能想在别处使用的公共名称空间(如XmlUtil)区分开来.
我认为与真正的匿名方法相比,这种方法的潜在缺点是,有人可能通过在其他模块中使用命名空间限定符来违反所需的静态范围.我不知道这是否是一个主要问题.
C++ 98标准不推荐使用static关键字.static的问题在于它不适用于类型定义.它也是在不同上下文中以不同方式使用的重载关键字,因此未命名的命名空间简化了一些事情.
根据经验,我只会注意到,虽然将以前的静态函数放入匿名命名空间是C++方式,但是较旧的编译器有时会遇到问题.我目前正在为我们的目标平台使用一些编译器,而更现代的Linux编译器可以将函数放入匿名命名空间.
但是在Solaris上运行的较旧的编译器,我们将在未指定的未来版本中使用,有时会接受它,有时会将其标记为错误.错误不是让我担心的问题,而是接受它时它可能正在做的事情.因此,在我们全面展现现代之前,我们仍然使用静态(通常是类范围的)函数,我们更喜欢匿名命名空间.