关于c ++:为什么使用未命名的命名空间,它们有什么好处?

Why are unnamed namespaces used and what are their benefits?

我刚刚加入了一个新的C++软件项目,我正在尝试理解这个设计。该项目经常使用未命名的命名空间。例如,类定义文件中可能会出现这样的情况:

1
2
3
4
5
6
7
8
// newusertype.cc
namespace {
  const int SIZE_OF_ARRAY_X;
  const int SIZE_OF_ARRAY_Y;
  bool getState(userType*,otherUserType*);
}

newusertype::newusertype(...) {...

可能导致使用未命名命名空间的设计注意事项是什么?有哪些优点和缺点?


(在下面,这些东西不再适用于C++ 11,而是应用于C++ 03。C++ 11几乎没有任何区别(如果有的话,它们只是语言律师的差异,我不记得了)。

未命名的名称空间是一个实用程序,用于使标识符转换单元成为本地的。它们的行为就好像您要为名称空间的每个翻译单元选择一个唯一的名称:

1
2
3
namespace unique { /* empty */ }
using namespace unique;
namespace unique { /* namespace body. stuff in here */ }

使用空主体的额外步骤很重要,因此您可以在命名空间主体内引用在该命名空间中定义的标识符,如::name,因为已经发生了using指令。

这意味着您可以拥有调用(例如)help的自由函数,这些函数可以存在于多个翻译单元中,并且在链接时不会发生冲突。其效果几乎与使用c中使用的static关键字相同,您可以将该关键字放入标识符声明中。未命名的名称空间是一种更好的选择,甚至能够使类型转换单元成为本地的。

1
2
namespace { int a1; }
static int a2;

两个a都是本地翻译单元,不会在链接时发生冲突。但区别在于匿名名称空间中的a1得到了一个唯一的名称。

阅读Comeau Computing的优秀文章:为什么使用未命名的命名空间而不是静态命名空间?(archive.org镜像)。


在匿名名称空间中拥有某个内容意味着它是这个翻译单元(.cpp文件及其所有include)的本地部分,这意味着如果在其他地方定义了另一个同名的符号,则不会违反一个定义规则(odr)。

这与具有静态全局变量或静态函数的C方式相同,但是它也可以用于类定义(并且应该在C++中使用而不是EDCOX1,0)。

同一文件中的所有匿名命名空间都被视为同一命名空间,不同文件中的所有匿名命名空间都是不同的。匿名命名空间等效于:

1
2
3
4
namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;


除了这个问题的其他答案外,使用匿名名称空间还可以提高性能。由于名称空间中的符号不需要任何外部链接,编译器可以更自由地对名称空间中的代码执行积极的优化。例如,一个循环中多次调用的函数可以被内联,而不会对代码大小产生任何影响。

例如,在我的系统上,如果使用匿名命名空间,以下代码将占用大约70%的运行时间(x86-64 gcc-4.6.3和-o2;请注意,add-val中的额外代码使编译器不希望将其包括两次)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>

namespace {
  double a;
  void b(double x)
  {
    a -= x;
  }
  void add_val(double x)
  {
    a += x;
    if(x==0.01) b(0);
    if(x==0.02) b(0.6);
    if(x==0.03) b(-0.1);
    if(x==0.04) b(0.4);
  }
}

int main()
{
  a = 0;
  for(int i=0; i<1000000000; ++i)
    {
      add_val(i*1e-10);
    }
  std::cout << a << '
'
;
  return 0;
}


示例显示,您加入的项目中的人员不了解匿名命名空间:)

1
2
3
namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

这些不需要在匿名名称空间中,因为const对象已经具有静态链接,因此不可能与另一个翻译单元中相同名称的标识符冲突。

1
2
    bool getState(userType*,otherUserType*);
}

这实际上是一种悲观情绪:getState()具有外部联系。通常最好选择静态链接,因为这样不会污染符号表。最好是写

1
static bool getState(/*...*/);

在这里。我掉进了同一个陷阱(标准中的措辞表明文件静力学在某种程度上被贬低为匿名的命名空间),但是在像KDE这样的大型C++项目中,你会得到很多人再次正确地改变你的方向:


匿名名称空间使封闭的变量、函数、类等仅在该文件中可用。在您的示例中,这是一种避免全局变量的方法。没有运行时或编译时性能差异。

除了"我希望这个变量、函数、类等是公共的还是私有的?"之外,没有什么优势或劣势。


未命名的命名空间将类、变量、函数和对象的访问权限限制在定义它的文件中。未命名的命名空间功能类似于C/C++中的EDCOX1×0关键字。static关键字限制全局变量和函数对定义它们的文件的访问。未命名命名空间和static关键字之间存在差异,因为未命名命名空间比静态命名空间具有优势。static关键字可以用于变量、函数和对象,但不能用于用户定义的类。例如:

1
static int x;  // Correct

但是,

1
2
static class xyz {/*Body of class*/} //Wrong
static structure {/*Body of structure*/} //Wrong

但对于未命名的命名空间也可以这样。例如,

1
2
3
4
 namespace {
           class xyz {/*Body of class*/}
           static structure {/*Body of structure*/}
  } //Correct