为什么使用未命名的命名空间,它们有什么好处?


242

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

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

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

有哪些设计考虑因素可能会导致使用未命名的命名空间?优点和缺点是什么?

Answers:


189

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

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镜像)。


您解释与的关系static。能否请您比较一下__attribute__ ((visibility ("hidden")))
菲茨

74

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

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

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

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;

14

未命名的命名空间将类,变量,函数和对象的访问权限限制在其定义文件中。未命名的名称空间功能类似于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

13

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

例如,在我的系统上,如果使用匿名名称空间,则以下代码将花费大约70%的运行时间(x86-64 gcc-4.6.3和-O2;请注意,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;
}

5
太不可思议了-我在gcc 4-1-2上使用O3优化在有和无命名空间语句的情况下尝试了该段:->获得了相同的时间(使用-O3时为3秒,使用-O3时为4秒)
Theo 2015年

2
这段代码故意复杂,试图说服编译器不要将b和add_val内联到main中。O3优化使用大量内联,而不考虑代码膨胀的代价。但是,仍有一些可能的函数在其中O3不能内联add_val。您可以尝试使add_val更复杂,或者在不同情况下从main多次调用它。
xioxox

5
@Daniel:我想念什么?如您所读,您说自己与-O3自身进行了比较,那么您说3到4秒是“同一时间”。这些都不有意义。我怀疑真正的解释会,但这是什么?
underscore_d 2015年

@underscore_d在两种情况下都使用-O2回答,而不是-O3。不同的优化级别可能会表现不同。另外,不同的编译器版本可能会表现不同(答案可能会过时,也就是)
Paul Stelian

1
@PaulStelian我知道,但很明显我不是在回应xioxox的回答,而是在回应Theo的评论(尽管他的名字已更改或我不知何故感到困惑)
underscore_d

12

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

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

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

    bool getState(userType*,otherUserType*);
}

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

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

这里。我陷入了同样的陷阱(标准中的措辞表明,不赞成使用文件静态方法,而赞成使用匿名名称空间),但是在像KDE这样的大型C ++项目中工作时,会遇到很多人以正确的方式前进再来一遍:)


10
由于C ++ 11个未命名空间具有内部链接(在标准或3.5节en.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces
埃米尔Vrijdags

11
“这些不必位于匿名命名空间中”从技术上来说,可以肯定-但仍然可以将它们放在一起,以直观地提醒它们的语义并将其(甚至更多)去除constness 并没有什么害处。以后再根据需要。我怀疑这意味着OP团队“什么都不懂”!另外,如前所述,在具有外部链接的匿名命名空间中,有关函数的说法是错误的。据我了解,它们解决了以前需要外部链接的模板参数问题,因此可以允许未命名的名称空间(能够包含模板args)具有内部链接。
underscore_d

11

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

除了“我希望此变量,函数,类等是公共的还是私有的”之外,没有什么优点或缺点。


2
可能会有性能差异-在此处查看我的答案。它使编译器可以更好地优化代码。
xioxox

2
你有一定道理; 至少就今天的C ++而言。但是,C ++ 98 / C ++ 03必需的东西具有外部链接,才能用作模板参数。由于匿名名称空间中的内容可用作模板参数,因此即使没有办法从文件外部引用它们,它们也将具有外部链接(至少在C ++ 11之前)。我认为可能对此有所怀疑,因为该标准仅要求事物的行为就像规则在被执行一样。有时可以在不真正执行规则的情况下做到这一点。
Max Lybbert 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.