【问题标题】:unnamed namespace within named namespace命名空间中的未命名空间
【发布时间】:2013-01-07 17:31:02
【问题描述】:

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

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

任何意见将不胜感激。

【问题讨论】:

    标签: c++ namespaces


    【解决方案1】:

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

    上面的代码之所以有效,是因为 foo 的定义在 same 翻译单元中。

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

    【讨论】:

    • both 示例都是这种情况。因此,您不是在描述 差异,也不是在描述一个相对于另一个的好处。
    • @LightnessRacesinOrbit:区别/好处是你称呼它的方式。使用建议的(顶部)方式,您可以调用XXX::some_function();,而使用您的建议,您必须像some_function(); 一样调用它不小心从全局范围调用了外部函数。
    • @GrimFandango 我知道 - 答案应该解释它。我的评论是对答案的批评,而不是寻求帮助。
    【解决方案2】:

    好的,原来是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

    【讨论】:

    • 我明白你在说什么。我应该明确的是,翻译单元中唯一的命名空间是X,所以我不认为它会有所作为。如果有其他命名空间或main 我可以理解,但老实说,在这种情况下,我会争辩重新排列代码翻译单元。谢谢。
    • “所以,不,没有什么实际好处。”。有实际的好处。来自未命名命名空间的名称不能从其他翻译单元访问,即使它们具有外部链接,除非它们被声明为 staticconst
    • @Nawaz: ... 原始代码和 OP 建议的新代码都是这种情况。因此不是考虑因素。
    • @LightnessRacesinOrbit:是的。但我只是对你的声明发表评论,尤其是在发布 ideone.com 链接后立即发表的评论;我认为您正在从您的 ideone 代码中得出一些结论。
    • @abhinav:是的,因为您是从同一个翻译(编译)单元访问它。
    【解决方案3】:

    从全局角度来看几乎没有什么好处:如果从其他翻译单元的角度来看,两种方法的结果相同:匿名命名空间是不可见的(或无法引用)。

    从同一个翻译单元的角度来看,有一个区别:定义顶级命名空间这一事实意味着您减少了导入在其他地方声明的命名空间冲突的可能性,最常见的是全局命名空间(无命名空间的函数,想想从 ISO C 继承的任何东西,比如从 stdio.h 或其他)。

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

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

    现在,如果您在命名命名空间中命名匿名命名空间,您将获得以下效果:

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

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

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

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

    而“b”的区别在于:它与命名空间 a { abort(); 不同。 } 因为它没有全局链接,所以你可以在另一个翻译单元中重新定义它而不会发生冲突。祝你好运尝试链接两个定义命名空间 a { 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++ 命名空间解决了这个问题,除非其他人不使用它,所以必须采取措施防止(这不是遗留代码的选项)或抵消它。

    【讨论】:

      【解决方案4】:

      在命名空间内使用未命名空间的好处之一是防止Argument dependent lookup (ADL)。 实际上,如果您在同一个命名空间中定义类型和函数,您可能会遇到undesirable effects。 下面的例子说明了这个问题:

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

      //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);
      }
      

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

      //header.h
      namespace mynamespace {
          struct mytype {};
      
          //OK prevents ADL
          namespace {
              static void myfunction(mytype){}
          }
      }
      

      【讨论】:

        【解决方案5】:

        一个简单的好处是,从命名命名空间中的匿名命名空间,可以直接访问命名命名空间的东西:

        namespace foo {
            class Bar;
        
            namespace {
                void Baz() {
                    Bar bar;  // no need to specify the foo namespace 
                    …
                }
            }
        
            …
        }
        

        代替:

        class foo::Bar;
        
        namespace {
            void Baz() {
                foo::Bar bar;
                …
            }
        }
        
        namespace foo {
            …
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-20
          • 2011-01-06
          • 1970-01-01
          • 1970-01-01
          • 2021-02-26
          相关资源
          最近更新 更多