题
一个特征C++的能力是创建无名(匿名)的名字空间,就像这样:
namespace {
int cannotAccessOutsideThisFile() { ... }
} // namespace
你会认为,这样一个特点将是没有用的-因为你可以不指定名称的空间,这是不可能访问的任何内容,它从外。但是这些不知名的命名空间 都 可在文件中他们创建的,因为如果你有一个隐含的使用条款。
我的问题是,为什么或者当这将是可取的,使用的是静态的功能吗?或是他们实质上的两种方式做同样的事情?
解决方案
C ++标准在第7.3.1.1节“未命名的命名空间”中进行了介绍,第2段:
使用static关键字是 在声明对象时不推荐使用 命名空间范围,未命名的命名空间 提供了一个更好的选择
静态仅适用于对象,函数和匿名联合的名称,而不适用于类型声明。
修改强>
弃用static关键字的这种使用(影响翻译单元中变量声明的可见性)的决定已被颠倒( ref )。在这种情况下,使用静态或未命名的命名空间基本上是两种完全相同的方式。有关更多讨论,请参阅此问题。
未命名的命名空间仍然具有允许您定义翻译单元本地类型的优点。有关更多详细信息,请参阅此 SO问题。
感谢 Mike Percy 引起我的注意。
其他提示
将方法放入匿名命名空间可防止您意外违反一个定义规则,永远不要担心命名你的帮助方法与你可能链接的其他方法相同。
而且,正如luke所指出的,匿名命名空间是静态成员的标准首选。
有一种边缘情况下,静止具有令人惊讶的作用(至少这是我)。C++03标准的国家14.6.4.2/1:
对于功能的电话,取决于一个模板参数,如果功能的名字是 不合格-id 但不是一个 模板-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),只能声明,现在的模板的定义或模板化背景下被发现。
结果是,这个特定的差异之间的静态和未命名的名字空间的成员不再存在。
我最近开始在我的代码中用匿名命名空间替换静态关键字但是立即遇到了一个问题,即命名空间中的变量不再可用于我的调试器中进行检查。我使用的是VC60,所以我不知道这是否与其他调试器没有问题。我的解决方法是定义一个“模块”命名空间,在那里我给它命名了我的cpp文件。
例如,在我的XmlUtil.cpp文件中,我为所有模块变量和函数定义了一个名称空间XmlUtil_I {...}。这样我就可以在调试器中应用XmlUtil_I :: qualified来访问变量。在这种情况下,'_I'将它与我可能想在其他地方使用的公共名称空间(如XmlUtil)区分开来。
我认为与真正的匿名方法相比,这种方法的一个潜在缺点是,有人可能通过在其他模块中使用命名空间限定符来违反所需的静态范围。我不知道这是否是一个主要问题。
C ++ 98标准不推荐使用static关键字。 static的问题在于它不适用于类型定义。它也是在不同上下文中以不同方式使用的重载关键字,因此未命名的命名空间简化了一些事情。
根据经验,我会注意到,虽然将以前静态函数放入匿名命名空间是C ++方式,但旧编译器有时会遇到问题。我目前正在为我们的目标平台使用一些编译器,而更现代的Linux编译器可以将函数放入匿名命名空间。
但是在Solaris上运行的旧编译器,我们将在未指定的未来版本中使用,有时会接受它,有时会将其标记为错误。错误并不是让我担心的问题,而是它接受时可能正在做的事情。因此,在我们全面开发之前,我们仍然使用静态(通常是类范围的)函数,我们更喜欢匿名命名空间。
此外,如果在变量上使用静态关键字,例如:
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及更高版本将为命名空间中的未引用函数生成警告,并对未使用的静态函数发出警告。
我个人喜欢静态功能在无名的名字空间的用于以下原因:
这是明显和明确的功能清单,它是私人的翻译单位,它的编制。与namesless名字空间,你可能需要以滚动和搜索来看,如果一个功能是在一个名字空间。
职能名称空间可能被视为外部通过了一些(老年)的编译器。在VS2017他们仍然是外部.由于这个原因,即使一个函数是在无名的名字空间,你可能仍然想要他们是静态的。
静态功能的表现非常类似的C或C++,而无名的命名空间显然也是用C++只。无名的命名空间也增加额外的水平,如果压痕和我不喜欢这样:)
因此,我高兴地看到,使用的静态功能 不废弃的了.