Hea*_*eek 486 c++ namespaces
C++的一个特性是能够创建未命名(匿名)命名空间,如下所示:
namespace {
int cannotAccessOutsideThisFile() { ... }
} // namespace
Run Code Online (Sandbox Code Playgroud)
您会认为这样的功能是无用的 - 因为您无法指定命名空间的名称,所以无法从外部访问其中的任何内容.但是这些未命名的命名空间可以在它们创建的文件中访问,就好像你有一个隐含的using子句.
我的问题是,为什么或何时使用静态函数会更好?或者他们基本上是两种做同样事情的方式?
luk*_*uke 321
C++标准在第7.3.1.1节"未命名的命名空间"中进行了介绍,第2段:
在声明命名空间作用域中的对象时,不推荐使用static关键字,unnamed-namespace提供了一个更好的替代方法.
静态仅适用于对象,函数和匿名联合的名称,而不适用于类型声明.
编辑:
弃用static关键字的这种使用(影响翻译单元中变量声明的可见性)的决定已被颠倒(ref).在这种情况下,使用静态或未命名的命名空间基本上是两种完全相同的方式.有关更多讨论,请参阅此 SO问题.
未命名的命名空间仍然具有允许您定义翻译单元本地类型的优点.有关更多详细信息,请参阅此 SO问题.
幸得迈克·珀西提出这个引起我的注意.
Ric*_*den 36
有一个边缘情况静态具有惊人的效果(至少对我而言).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);
}
Run Code Online (Sandbox Code Playgroud)
本身这可能不是什么大不了的事,但它确实强调了对于完全兼容的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);
}
Run Code Online (Sandbox Code Playgroud)
确保在使用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)的查找部分,仅找到在模板定义上下文或模板实例化上下文中找到的函数声明.
结果是静态和未命名的命名空间成员之间的这种特殊差异不再存在.
小智 11
我最近开始在我的代码中用匿名命名空间替换静态关键字,但是立即遇到了一个问题,即命名空间中的变量不再可用于我的调试器中进行检查.我使用的是VC60,所以我不知道这是否与其他调试器没有问题.我的解决方法是定义一个"模块"命名空间,在那里我给它命名了我的cpp文件.
例如,在我的XmlUtil.cpp文件中,我为所有模块变量和函数定义了一个名称空间XmlUtil_I {...}.这样我就可以在调试器中应用XmlUtil_I :: qualified来访问变量.在这种情况下,'_ I'将它与我可能想在别处使用的公共名称空间(如XmlUtil)区分开来.
我认为与真正的匿名方法相比,这种方法的潜在缺点是,有人可能通过在其他模块中使用命名空间限定符来违反所需的静态范围.我不知道这是否是一个主要问题.
Lew*_*sey 10
区别在于重整标识符的名称(_ZN12_GLOBAL__N_11bEvs _ZL1b,这并不重要,但它们都被组装为符号表中的本地符号(缺少.globalasm 指令)。
#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
Run Code Online (Sandbox Code Playgroud)
对于嵌套的匿名命名空间:
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
Run Code Online (Sandbox Code Playgroud)
翻译单元中所有第一级匿名命名空间相互组合,翻译单元中所有第二级嵌套匿名命名空间相互组合
您还可以在匿名命名空间中拥有嵌套命名空间或嵌套内联命名空间
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
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
//inline has the same output
Run Code Online (Sandbox Code Playgroud)
您还可以拥有匿名内联命名空间,但据我所知,inline对匿名命名空间的影响为 0
inline namespace {
inline namespace {
int a = 3;
}
}
Run Code Online (Sandbox Code Playgroud)
_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),名为A,前缀长度为A1。匿名命名空间都有这个名字_GLOBAL__N_1
C++ 98标准不推荐使用static关键字.static的问题在于它不适用于类型定义.它也是在不同上下文中以不同方式使用的重载关键字,因此未命名的命名空间简化了一些事情.
根据经验,我只会注意到,虽然将以前的静态函数放入匿名命名空间是C++方式,但是较旧的编译器有时会遇到问题.我目前正在为我们的目标平台使用一些编译器,而更现代的Linux编译器可以将函数放入匿名命名空间.
但是在Solaris上运行的较旧的编译器,我们将在未指定的未来版本中使用,有时会接受它,有时会将其标记为错误.错误不是让我担心的问题,而是接受它时它可能正在做的事情.因此,在我们全面展现现代之前,我们仍然使用静态(通常是类范围的)函数,我们更喜欢匿名命名空间.
| 归档时间: |
|
| 查看次数: |
199665 次 |
| 最近记录: |