未命名/匿名命名空间与静态函数
C ++的一个小function就是能够创build匿名(匿名)的命名空间,如下所示:
namespace { int cannotAccessOutsideThisFile() { ... } } // namespace
你会认为这样的function是无用的 – 因为你不能指定名字空间的名字,所以不可能从外部访问它的任何东西。 但是这些无名的命名空间可以在它们创build的文件中访问,就好像你有一个隐式的using子句。
我的问题是,为什么或什么时候这会比使用静态函数更可取? 或者他们基本上是两种做同样的事情?
C ++标准读入7.3.1.1节未命名的命名空间,第2节:
在声明名称空间作用域中的对象时不推荐使用static关键字,unnamed名称空间提供了一个更好的select。
静态只适用于对象,函数和匿名联合的名称,而不是types声明。
编辑:
已经颠倒了决定不使用静态关键字(影响翻译单元中variables声明的可见性)( ref )。 在这种情况下,使用静态名称空间或未命名空间回到基本上是做同样的事情的两种方式。 有关更多讨论,请参阅此 SO问题。
未命名的名称空间仍然具有允许您定义翻译单元本地types的优点。 请看到这个问题的更多细节。
值得一提的是麦克·珀西 ( Mike Percy)提出的这个问题。
把方法放在一个匿名的命名空间中可以防止你意外地违反了One Definition Rule ,这样你就不必担心命名你的助手方法和你链接的其他方法一样。
而且,正如卢克所指出的那样,匿名的名字空间比标准静态成员更受欢迎。
有一个边缘情况下,静态有一个惊人的影响(至less对我来说)。 C ++ 03标准在14.6.4.2/1中说明:
对于依赖于模板参数的函数调用,如果函数名称是非限定标识符,但不是模板标识符 ,则使用通常的查找规则(3.4.1,3.4.2)find候选函数,除了:
- 对于使用非限定名称查找(3.4.1)的查找部分,只能find具有来自模板定义上下文的外部链接的函数声明。
- 对于使用关联名称空间(3.4.2)的查找部分,只能find在模板定义上下文或模板实例化上下文中find的具有外部链接的函数声明。
…
下面的代码将会像你期望的那样调用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
关键字仍然具有不可用的任何其他方式的function。
// 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的模板中find的方法是使其成为static
。
现代C ++的更新
从C ++ '11开始,一个匿名名字空间的成员隐式地有内部连接(3.5 / 4):
在未命名名称空间内直接或间接声明的未命名名称空间或名称空间具有内部链接。
但同时,14.6.4.2/1更新了删除链接的提及(这取自C ++ '14):
对于postfix-expression是从属名称的函数调用,候选函数是使用通常的查找规则(3.4.1,3.4.2)find的,除了:
对于使用非限定名称查找(3.4.1)的查找部分,只能find来自模板定义上下文的函数声明。
对于使用关联名称空间(3.4.2)的查找部分,只能find在模板定义上下文或模板实例化上下文中find的函数声明。
结果是静态和未命名的名称空间成员之间的这种特殊的区别不再存在。
我最近开始用代码中的匿名命名空间replace静态关键字,但是立即遇到了一个问题,即在我的debugging器中名称空间中的variables不再可用于检查。 我正在使用VC60,所以我不知道这是否与其他debugging器没有问题。 我的解决方法是定义一个“模块”名称空间,我给它的名称我的CPP文件。
例如,在我的XmlUtil.cpp文件中,我为我所有的模块variables和函数定义了一个命名空间XmlUtil_I {…}。 这样我可以在debugging器中应用XmlUtil_I ::资格来访问variables。 在这种情况下,'_I'区别于我可能想在别处使用的公共名称空间,如XmlUtil。
我认为这种方法与真正的匿名方法相比有一个潜在的缺点,那就是有人可能通过在其他模块中使用名称空间限定符来违反所需的静态作用域。 我不知道这是否是一个主要的问题。
为此目的使用static关键字被C ++ 98标准所弃用。 静态的问题是它不适用于types定义。 这也是在不同的上下文中以不同方式使用的重载关键字,所以未命名的命名空间可以简化一些事情。
根据经验,我只会注意到,虽然将以前的静态函数放入匿名名称空间是一种C ++方式,但是较早的编译器有时可能会遇到问题。 我目前正在为我们的目标平台使用一些编译器,更现代的Linux编译器可以把函数放入匿名的命名空间。
但是,在Solaris上运行的一个较旧的编译器,我们将被绑定到一个未指定的将来的版本,有时会接受它,有时会将其标记为错误。 错误并不是我担心的事情,它接受它时可能会这样做。 所以,直到我们全盘现代化,我们仍然使用静态(通常是类范围)函数,我们更喜欢匿名命名空间。
在阅读你的问题的时候,我只知道这个function,我只能推测。 这似乎比文件级静态variables提供了几个优点:
- 匿名命名空间可以互相嵌套,提供多重保护,从中避免符号溢出。
- 几个匿名命名空间可以放在同一个源文件中,在同一个文件中创build不同的静态级别范围。
如果有人在实际代码中使用匿名命名空间,我会对学习感兴趣。
另外,如果在这个例子中使用了一个variables的static关键字:
namespace { static int flag; }
它不会在映射文件中看到
编译器特定的区别匿名命名空间和静态函数可以看到编译下面的代码。
#include <iostream> namespace { void unreferenced() { std::cout << "Unreferenced"; } void referenced() { std::cout << "Referenced"; } } static void static_unreferenced() { std::cout << "Unreferenced"; } static void static_referenced() { std::cout << "Referenced"; } int main() { referenced(); static_referenced(); return 0; }
使用VS 2017编译此代码(指定级别4警告标志/ W4以启用警告C4505:未引用的本地函数已被删除 )以及带有-Wunused-function或-Wall标志的gcc 4.9显示VS 2017只会产生警告未使用的静态function。 gcc 4.9及更高版本以及3.3及更高版本将在命名空间中对未引用的函数产生警告,并且还会为未使用的静态函数发出警告。
现场演示gcc 4.9和MSVC 2017