命名空间中的未命名命名空间

时间:2013-01-07 17:31:03

标签: c++ namespaces

我被要求修改的一些代码看起来像这样:

namespace XXX {

namespace {

// some stuff

} // end of unnamed

// Some stuff within the scope of XXX

} // end of XXX

我很难看到将未命名的命名空间嵌入另一个命名空间的优势,我正在考虑将其更改为:

namespace {

// some stuff

} // end of unnamed

namespace XXX {

// Some stuff within the scope of XXX

} // end of XXX

非常感谢任何观点。

4 个答案:

答案 0 :(得分:13)

它确实有实际的好处。 未命名的命名空间会在不同的翻译单元中隐藏其中的名称。

上述代码的作用仅仅是因为foo的定义在相同转换单元中。

假设main()和foo()的定义在不同的翻译单元中。 它会编译,因为主文件包含声明的标题。但它不会链接,因为逻辑上没有X :: ((未命名的命名空间):: foo这样的东西。

答案 1 :(得分:6)

从全球观点来看,收益很少:如果从其他翻译单位的角度来看,两种方法都有相同的结果:匿名命名空间是不可见的(或者不能被引用)。

从相同的翻译单元的角度来看,存在差异:您定义顶级命名空间的事实意味着您降低了导入在其他地方声明的命名空间冲突的可能性,并且最常见的是全局命名空间命名空间(无命名空间的函数,从stdio.h或其他任何东西想想从ISO C继承的任何东西)。

例如,如果您在该翻译单元中导入的全局标题具有“无名称空间”abort()并且您在翻译单元中声明了名称空间{abort(){...}},则会产生歧义,例如,gcc会抛出一个编译错误:

error: call of overloaded ‘abort()’ is ambiguous

现在,如果在命名命名空间中命名匿名命名空间,则会产生以下影响:

a)在命名空间内声明的函数没有歧义,因为它具有优先权:

namespace a { namespace { abort() {...} } }

如果你有一个像:: whatever()这样的函数并且它引用了abort(),那么它将在它自己的命名空间中解析,因为它具有优先权。

b)你不会拥有:: abort()的全局链接,因为它不存在于翻译单元之外,与命名空间{abort();相同在顶层,但没有上面的潜在冲突。

在“b”中存在差异:它与命名空间a {abort();不同因为它没有全局链接,所以你可以在没有冲突的情况下在另一个翻译单元中重新定义它。祝你试图链接两个定义命名空间的翻译单元{abort(){...}} ...

所以你完全按照你的意思得到:

namespace a { // you have a named space, so you don't have conflicts with the nameless one
  namespace { // but you have local visibility and linkage
    whatever(); // for this
  }
}

简而言之:两种方式都有相似之处,但存在差异。有人可能认为这不是很有用,但作为一种风格,它会先发制人地避免与全局命名空间的冲突。人们仍然可以争辩说,因为这些将在编译时捕获(希望,至少当签名完全匹配时),为什么还要费心。但是,如果您的项目是一个可移植的库,并且您的标头可能会受到污染,这取决于环境标头自身导入的内容,这是一个有用的概念,否则您的用户将不得不为您的系统修补您的库,或者您需要#ifdefs到处都是。

我在ISO / ANSI C 99上编程很多,有时候我必须做以下事情:

#include <headerA.h>
#define symbol symbolB
#include <headerB.h>
// or some crap alike. And I have linker problems with above.

...因为两个标题(来自例如不同的库)设法污染命名空间,我不能简单地修补别人的库。

C ++命名空间解析了这一点,除非其他人不使用它,所以必须采取措施防止(这不是遗留代码的选项)或抵消它。 / p>

答案 2 :(得分:5)

好的,结果是X::<anonymous>::foo() is visible as X::foo()。我很惊讶。

所以,不,实际上没有什么好处。但是可能存在语义或文档含义。


原始答案

那相当取决于“东西”,不是吗?

现有代码允许X中的代码拥有“私有”其他内容,这些内容也位于X但无法从X外部访问:

#include <iostream>

namespace X {
   namespace {
      void foo() { std::cout << "lol\n"; }
   }

   void bar() { foo(); }
}

int main()
{
   X::bar();
   // X::foo();  // can't do this directly  [edit: turns out we can!]
}
  • 输出:lol\n

您提出的方法使整个翻译单位可以使用“私人资料”:

#include <iostream>

namespace {
   void foo() { std::cout << "lol\n"; }
}

namespace X {
   void bar() { foo(); }
}

int main()
{
   X::bar();
   foo();     // works
}
  • 输出:lol\nlol\n

答案 3 :(得分:0)

在命名空间中使用未命名空间的好处之一是可以防止Argument dependent lookup (ADL)。 确实,如果在相同的命名空间中定义类型和函数,则可能会遇到undesirable effects。 以下示例说明了该问题:

假设我们有一个简单的头文件,如下所示,其中我们在命名空间myfunction中为自定义类型mytype定义了通用函数mynamespace

//header.h
namespace mynamespace {
    struct mytype {};

    static void myfunction(mytype){}
}

现在,在另一个文件test.cpp中,我们希望出于特定目的覆盖myfunction(例如,打印调试信息,执行其他操作,I18N ...),而无需更改程序的其余部分。以下代码不会与我们的头文件冲突,因为它是一个不同的命名空间,即全局命名空间。

//test.cpp
#include "header.h"

static void myfunction(mynamespace::mytype){}

int main(){
    mynamespace::mytype val = {};
    //error: call of overloaded 'myfunction(mynamespace::mytype&)' is ambiguous
    myfunction(val);
}

令人惊讶的是,由于ADL,对以上myfunction的调用是模棱两可的并且无法编译。 为了解决您的问题,您可以使用匿名命名空间来阻止ADL。

//header.h
namespace mynamespace {
    struct mytype {};

    //OK prevents ADL
    namespace {
        static void myfunction(mytype){}
    }
}