308

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

// newusertype.cc
namespace {
  const int SIZE_OF_ARRAY_X;
  const int SIZE_OF_ARRAY_Y;
  bool getState(userType*,otherUserType*);
}

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

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

4

6 回答 6

241

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

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

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

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

namespace { int a1; }
static int a2;

两者a都是本地翻译单元,不会在链接时发生冲突。但不同的是,a1匿名命名空间中的 获得了唯一的名称。

阅读 comeau-computing 上的优秀文章为什么使用未命名的命名空间而不是静态的?Archive.org 镜像)。

于 2008-12-10T20:19:20.320 回答
108

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

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

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

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;
于 2008-12-10T20:08:44.537 回答
24

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

static int x;  // Correct 

但,

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

但是对于未命名的命名空间也是可能的。例如,

 namespace {
           class xyz {/*Body of class*/}
           static structure {/*Body of structure*/}
  } //Correct
于 2016-05-26T16:56:48.297 回答
16

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

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

#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 << '\n';
  return 0;
}
于 2014-08-29T09:19:10.257 回答
13

该示例表明您加入的项目中的人不了解匿名命名空间:)

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

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

    bool getState(userType*,otherUserType*);
}

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

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

这里。我陷入了同样的陷阱(标准中的措辞表明文件静态以某种方式被弃用以支持匿名命名空间),但是在像 KDE 这样的大型 C++ 项目中工作,你会得到很多人把你的头转向正确的方式又来了:)

于 2009-07-24T17:20:04.520 回答
12

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

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

于 2008-12-10T20:10:03.707 回答