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

Why are unnamed namespaces used and what are their benefits?

提问人:Scottie T 提问时间:12/11/2008 最后编辑:James McNellisScottie T 更新时间:2/15/2023 访问量:176102

问:

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

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

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

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

C++ OOP 命名空间

评论


答:

131赞 Motti 12/11/2008 #1

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

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

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

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;
14赞 Max Lybbert 12/11/2008 #2

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

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

评论

3赞 xioxox 8/29/2014
可能存在性能差异 - 请在此处查看我的答案。它允许编译器更好地优化代码。
3赞 Max Lybbert 8/29/2014
你说得有道理;至少就今天的C++而言。但是,C++98/C++03 必需的东西具有外部链接,以便用作模板参数。由于匿名命名空间中的内容可用作模板参数,因此即使无法从文件外部引用它们,它们也会具有外部链接(至少在 C++11 之前)。我认为可能有一些捏造的能力,因为标准只要求事情表现得好像规则被执行了一样;有时有可能在不真正执行规则的情况下做到这一点。
276赞 Johannes Schaub - litb 12/11/2008 #3

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

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

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

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

namespace { int a1; }
static int a2;

两者都是本地翻译单元,在链接时不会发生冲突。但不同之处在于,匿名命名空间中有一个唯一的名称。aa1

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

评论

1赞 phinz 3/22/2020
您解释了与 的关系。你能不能也比较一下?static__attribute__ ((visibility ("hidden")))
3赞 raylu 12/15/2022
与科莫的链接似乎不再起作用了
16赞 Marc Mutz - mmutz 7/25/2009 #4

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

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

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

    bool getState(userType*,otherUserType*);
}

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

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

这里。我落入了同样的陷阱(标准中有一些措辞表明文件静态在某种程度上被弃用,转而支持匿名命名空间),但是在像 KDE 这样的大型C++项目中工作,你会得到很多人再次转向正确的方向:)

评论

13赞 Emile Vrijdags 4/6/2015
由于 c++11 未命名的命名空间具有内部链接(标准或 en.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces 中的第 3.5 节)
14赞 underscore_d 7/10/2016
“这些不需要在匿名命名空间中” 从技术上讲,当然 - 但是,将它们放在一个中并没有什么坏处,作为它们语义的视觉提醒,并使它(甚至更)微不足道,如果需要的话,以后删除ness。我怀疑这意味着 OP 的团队“不明白”任何事情!此外,如前所述,在C++11及以后,关于具有外部链接的匿名命名空间中的函数是错误的。据我了解,他们修复了以前需要外部链接的模板参数问题,因此可以允许未命名的命名空间(能够包含模板参数)具有内部链接。const
19赞 xioxox 8/29/2014 #5

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

例如,在我的系统上,如果使用匿名命名空间,以下代码将占用大约 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;
}

评论

6赞 Theo 11/4/2015
好得令人难以置信 - 我在 gcc 4-1-2 上尝试了这段,使用 O3 优化,有和没有命名空间语句:-> 得到相同的时间(3 秒,使用 -O3,4 秒使用 -O3)
2赞 xioxox 11/4/2015
这段代码故意复杂化,试图说服编译器不要将 b 和 add_val 内联到 main 中。O3 优化使用大量内联,而不管代码膨胀的成本如何。但是,O3 仍然可能add_val内联的功能。您可以尝试使add_val更复杂,或者在不同情况下从 main 多次调用它。
6赞 underscore_d 12/25/2015
@Daniel:我错过了什么?如上所述,您说您与自己比较,然后您说 3 与 4 秒是“相同的时间”。这些都没有一点意义。我怀疑真正的解释会,但它是什么?-O3
0赞 Paul Stelian 8/20/2018
@underscore_d 答案指出在这两种情况下都使用了 -O2,而不是 -O3。不同的优化级别可能表现不同。此外,不同的编译器版本可能表现不同(答案可能会过时,也就是说)
1赞 underscore_d 8/29/2018
@PaulStelian我知道这一点,但很明显,我不是在回答 xioxox 的回答,而是在回答 Theo 的评论(尽管他的名字已经改变,或者我不知何故混淆了)
35赞 Sachin 5/27/2016 #6

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

static int x;  // Correct 

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

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

 namespace {
           class xyz{/*Body of class*/}
           struct structure {/*Body of structure*/}
  } //Correct

                   

评论

4赞 smac89 11/27/2020
什么?另外,为什么它必须是静态的?static structure
3赞 adentinger 1/6/2022
@smac89 他们可能是指.static struct structure