未命名/匿名命名空间与静态函数

C ++的一个特性是能够创建未命名(匿名)的命名空间,如下所示:

namespace {
    int cannotAccessOutsideThisFile() { ... }
} // namespace

你会认为这样的功能是无用的 - 因为你不能指定名称空间的名称,所以不可能从外部访问它的任何内容。 但是,这些未命名的命名空间可以在创建它们的文件中访问,就好像你对它们有一个隐含的使用子句。

我的问题是,为什么或什么时候使用静态函数更可取? 或者他们基本上是两种做同样事情的方式?


C ++标准读入7.3.1.1节未命名的命名空间,第2节:

在声明名称空间作用域中的对象时,不推荐使用static关键字,unnamed名称空间提供了一种更好的选择。

静态仅适用于对象,函数和匿名联合的名称,不适用于声明类型。

编辑:

已经颠倒了决定不使用静态关键字(影响翻译单元中变量声明的可见性)(ref)。 在这种情况下,使用静态名称空间或未命名空间回到基本上完成同样事情的两种方式。 有关更多讨论,请参阅此SO问题。

未命名的名称空间仍然具有允许您定义翻译单元本地类型的优点。 有关更多详细信息,请参阅此SO问题。

值得一提的是麦克珀西将这引起了我的注意。


将方法放入匿名命名空间可防止您意外违反One Definition Rule,从而不必担心将辅助方法命名为与可能链接的其他方法相同。

而且,正如卢克指出的那样,匿名命名空间是标准优先于静态成员的。


有一个边缘情况下静态有一个惊人的影响(至少对我来说)。 C ++ 03标准在14.6.4.2/1中声明:

对于依赖于模板参数的函数调用,如果函数名称是非限定标识但不是模板标识,则使用通常的查找规则(3.4.1,3.4.2)找到候选函数,除了:

  • 对于使用非限定名称查找(3.4.1)的查找部分,只能找到具有来自模板定义上下文的外部链接的函数声明。
  • 对于使用关联名称空间(3.4.2)的查找部分,只能找到在模板定义上下文或模板实例化上下文中找到的具有外部链接的函数声明。
  • ...

    如你所期望的,下面的代码将调用foo(void*)而不是foo(S const &)

    template <typename T>
    int b1 (T const & t)
    {
      foo(t);
    }
    
    namespace NS
    {
      namespace
      {
        struct S
        {
        public:
          operator void * () const;
        };
    
        void foo (void*);
        static void foo (S const &);   // Not considered 14.6.4.2(b1)
      }
    
    }
    
    void b2()
    {
      NS::S s;
      b1 (s);
    }
    

    本身这可能不是什么大不了的事情,但它确实强调了对于完全兼容的C ++编译器(即支持export编译器), static关键字仍然具有以其他方式不可用的功能。

    // bar.h
    export template <typename T>
    int b1 (T const & t);
    
    // bar.cc
    #include "bar.h"
    template <typename T>
    int b1 (T const & t)
    {
      foo(t);
    }
    
    // foo.cc
    #include "bar.h"
    namespace NS
    {
      namespace
      {
        struct S
        {
        };
    
        void foo (S const & s);  // Will be found by different TU 'bar.cc'
      }
    }
    
    void b2()
    {
      NS::S s;
      b1 (s);
    }
    

    确保我们未命名的名称空间中的函数不会在使用ADL的模板中找到的唯一方法是将其设置为static

    现代C ++的更新

    从C ++ '11开始,未命名名称空间的成员隐含地具有内部链接(3.5 / 4):

    直接或间接在未命名名称空间内声明的未命名名称空间或名称空间具有内部链接。

    但与此同时,14.6.4.2/1更新了删除连接的提及(这取自C ++ '14):

    对于postfix-expression是从属名称的函数调用,候选函数可以使用通常的查找规则(3.4.1,3.4.2)找到,除了:

  • 对于使用非限定名称查找(3.4.1)的查找部分,只能找到来自模板定义上下文的函数声明。

  • 对于使用关联名称空间(3.4.2)的查找部分,只能找到在模板定义上下文或模板实例化上下文中找到的函数声明。

  • 结果是静态和未命名的名称空间成员之间的这种特殊差别不再存在。

    链接地址: http://www.djcxy.com/p/95245.html

    上一篇: Unnamed/anonymous namespaces vs. static functions

    下一篇: Is there a "concise" way to do namespacing in JavaScript?