Answers:
C ++标准的内容在第7.3.1.1节“未命名的名称空间”的第2段中:
在命名空间范围内声明对象时,不建议使用static关键字,unnamed-namespace提供了一种更好的选择。
静态仅适用于对象,函数和匿名联合的名称,不适用于类型声明。
编辑:
反对使用static关键字的决定(影响翻译单元中变量声明的可见性)的决定已被撤销(ref)。在这种情况下,使用静态或未命名的名称空间实际上又是两种完全相同的方法。更多讨论,请参见本 SO问题。
未命名的命名空间仍然具有允许您定义本地翻译单元类型的优点。请参阅此 SO问题以获取更多详细信息。
幸得迈克·珀西提出这个引起我的注意。
namespace
隐式具有内部链接,因此应该没有区别。通过在C ++ 11中将其作为要求,可以解决以前可能由于措辞不当而引起的任何问题。
将方法放在匿名名称空间中可防止您意外违反“ 一个定义规则”,从而使您永远不必担心将您的辅助方法命名为与您可能链接的其他方法相同。
而且,正如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 &)
您期望的那样。
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在模板中找不到我们未命名名称空间中的函数的唯一方法是使用它static
。
现代C ++更新
从C ++ '11开始,未命名名称空间的成员具有隐式内部链接(3.5 / 4):
未命名的名称空间或在未命名的名称空间中直接或间接声明的名称空间具有内部链接。
但与此同时,更新了14.6.4.2/1,以消除对链接的提及(摘自C ++ '14):
对于后缀表达式为从属名称的函数调用,将使用常规查找规则(3.4.1、3.4.2)查找候选函数,但以下情况除外:
对于使用非限定名称查找(3.4.1)的查找部分,仅从模板定义上下文中找到函数声明。
对于使用关联的命名空间(3.4.2)进行的查找,仅会找到在模板定义上下文或模板实例化上下文中找到的函数声明。
结果是静态名称空间成员和未命名名称空间成员之间的特定区别不再存在。
NS::S
工作,不必不在S
里面namespace {}
吗?
我最近开始在代码中用匿名名称空间替换静态关键字,但立即遇到一个问题,即名称空间中的变量不再可在调试器中检查。我使用的是VC60,所以我不知道这是否与其他调试器无关。我的解决方法是定义一个“模块”命名空间,在该命名空间中为其命名为cpp文件的名称。
例如,在XmlUtil.cpp文件中,我XmlUtil_I { ... }
为所有模块变量和函数定义了名称空间。这样,我可以XmlUtil_I::
在调试器中应用限定条件来访问变量。在这种情况下,_I
区别于公共命名空间,例如XmlUtil
我可能想在其他地方使用的命名空间。
与真正匿名的方法相比,我认为这种方法的潜在缺点是,有人可能会在其他模块中使用名称空间限定符来违反所需的静态范围。我不知道这是否是一个主要问题。
#if DEBUG namespace BlahBlah_private { #else namespace { #endif
,所以“模块名称空间”仅出现在调试版本中,否则使用真正的匿名名称空间。如果调试器提供了一种解决此问题的好方法,那就太好了。Doxygen也被它弄糊涂了。
为此,C ++ 98标准不建议使用static关键字。静态的问题在于它不适用于类型定义。它也是一个重载的关键字,在不同的上下文中以不同的方式使用,因此未命名的名称空间会简化一些事情。
另外,如果像这样的示例在变量上使用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仅针对以下内容生成警告未使用的静态函数。gcc 4.9和更高版本以及clang 3.3和更高版本,将为命名空间中未引用的函数产生警告,并为未使用的静态函数产生警告。
就我个人而言,出于以下原因,我更喜欢静态函数而不是无名命名空间:
仅从函数定义就可以明显而清晰地看出,它对于编译所在的翻译单元是私有的。使用无名命名空间,您可能需要滚动和搜索以查看函数是否在命名空间中。
某些(较旧的)编译器可能会将名称空间中的函数视为外部函数。在VS2017中,它们仍然是外部的。因此,即使函数位于无名命名空间中,您仍可能希望将它们标记为静态。
静态函数在C或C ++中的行为非常相似,而无名命名空间显然仅是C ++。无名命名空间还增加了缩进级别,我不喜欢这样:)
static
实际上,只有关键字将局部链接应用于函数。而且,肯定只有疯狂的疯子才会为命名空间添加缩进吗?
我只是在阅读您的问题时才了解此功能,所以只能推测一下。与文件级静态变量相比,这似乎具有一些优点:
我会对是否有人在真实代码中使用匿名名称空间感兴趣。
区别在于整齐的标识符的名称(_ZN12_GLOBAL__N_11bE
vs _ZL1b
,这并不重要,但是它们都被组装为符号表中的本地符号(缺少.global
asm指令)。
#include<iostream>
namespace {
int a = 3;
}
static int b = 4;
int c = 5;
int main (){
std::cout << a << b << c;
}
.data
.align 4
.type _ZN12_GLOBAL__N_11aE, @object
.size _ZN12_GLOBAL__N_11aE, 4
_ZN12_GLOBAL__N_11aE:
.long 3
.align 4
.type _ZL1b, @object
.size _ZL1b, 4
_ZL1b:
.long 4
.globl c
.align 4
.type c, @object
.size c, 4
c:
.long 5
.text
至于嵌套的匿名名称空间:
namespace {
namespace {
int a = 3;
}
}
.data
.align 4
.type _ZN12_GLOBAL__N_112_GLOBAL__N_11aE, @object
.size _ZN12_GLOBAL__N_112_GLOBAL__N_11aE, 4
_ZN12_GLOBAL__N_112_GLOBAL__N_11aE:
.long 3
.align 4
.type _ZL1b, @object
.size _ZL1b, 4
翻译单元中的所有第一级匿名名称空间相互组合,翻译单元中的所有第二层嵌套匿名名称空间相互组合
您还可以在匿名名称空间中具有嵌套(内联)名称空间
namespace {
namespace A {
int a = 3;
}
}
.data
.align 4
.type _ZN12_GLOBAL__N_11A1aE, @object
.size _ZN12_GLOBAL__N_11A1aE, 4
_ZN12_GLOBAL__N_11A1aE:
.long 3
.align 4
.type _ZL1b, @object
.size _ZL1b, 4
which for the record demangles as:
.data
.align 4
.type (anonymous namespace)::A::a, @object
.size (anonymous namespace)::A::a, 4
(anonymous namespace)::A::a:
.long 3
.align 4
.type b, @object
.size b, 4
您还可以具有匿名内联名称空间,但据我所知,inline
在匿名名称空间上具有0作用
inline namespace {
inline namespace {
int a = 3;
}
}
_ZL1b
:_Z
表示这是错误的标识符。L
表示它是的本地符号static
。1
是标识符的长度,b
然后是标识符b
_ZN12_GLOBAL__N_11aE
_Z
表示这是错误的标识符。N
意味着这是一个名称空间,12
是匿名名称空间名称的长度_GLOBAL__N_1
,然后是匿名名称空间名称_GLOBAL__N_1
,然后1
是标识符的长度a
,a
是标识符a
并E
关闭驻留在名称空间中的标识符。
_ZN12_GLOBAL__N_11A1aE
与上面相同,除了其中还有另一个名称空间级别 1A
static
中,不赞成在这种情况下使用;尽管无名命名空间是一种更好的选择static
,但在某些情况下,static
抢救失败。