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

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

您可能会认为这样的特性毫无用处——因为不能指定名称空间的名称,因此不可能从外部访问其中的任何内容。但是,这些未命名的名称空间可以在创建它们的文件中访问,就像对它们有一个隐式的using子句一样。

我的问题是,为什么或者什么时候这比使用静态函数更可取?或者它们本质上是做同一件事的两种方式?


当前回答

在第7.3.1.1节第2段中,c++标准如下:

static关键字的用法是 类中声明对象时不支持 命名空间范围,即未命名的命名空间 提供了一个更好的选择。

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

编辑:

反对使用static关键字(影响翻译单元中变量声明的可见性)的决定已被推翻(ref)。在这种情况下,使用静态名称空间或未命名名称空间本质上是做完全相同的事情的两种方式。更多讨论请参见这个SO问题。

未命名的名称空间仍然具有允许您定义翻译单元局部类型的优势。请参阅这个SO问题了解更多细节。

感谢迈克·珀西让我注意到这一点。

其他回答

在第7.3.1.1节第2段中,c++标准如下:

static关键字的用法是 类中声明对象时不支持 命名空间范围,即未命名的命名空间 提供了一个更好的选择。

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

编辑:

反对使用static关键字(影响翻译单元中变量声明的可见性)的决定已被推翻(ref)。在这种情况下,使用静态名称空间或未命名名称空间本质上是做完全相同的事情的两种方式。更多讨论请参见这个SO问题。

未命名的名称空间仍然具有允许您定义翻译单元局部类型的优势。请参阅这个SO问题了解更多细节。

感谢迈克·珀西让我注意到这一点。

将方法放在匿名名称空间中可以防止您意外地违反One Definition Rule,允许您不必担心将您的helper方法命名为与您可能链接的其他方法相同的方法。

而且,正如luke所指出的,标准更喜欢匿名名称空间而不是静态成员。

此外,如果在变量上使用static关键字,例如:

namespace {
   static int flag;
}

在映射文件中不会看到它

就我个人而言,我更喜欢静态函数而不是无名称的名称空间,原因如下:

It's obvious and clear from function definition alone that it's private to the translation unit where it's compiled. With nameless namespace you might need to scroll and search to see if a function is in a namespace. Functions in namespaces might be treated as extern by some (older) compilers. In VS2017 they are still extern. For this reason even if a function is in nameless namespace you might still want to mark them static. Static functions behave very similar in C or C++, while nameless namespaces are obviously C++ only. nameless namespaces also add extra level in indentation and I don't like that :)

所以,我很高兴看到静态函数不再被弃用了。

在一种边缘情况下,静电会产生令人惊讶的效果(至少对我来说是这样)。c++ 03标准在14.6.4.2/1中声明:

For a function call that depends on a template parameter, if the function name is an unqualified-id but not a template-id, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2) except that: For the part of the lookup using unqualified name lookup (3.4.1), only function declarations with external linkage from the template definition context are found. For the part of the lookup using associated namespaces (3.4.2), only function declarations with external linkage found in either the template definition context or the template instantiation context are found. ...

下面的代码将调用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++编译器(即支持导出的编译器),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的模板中找到的唯一方法是将其设置为静态。

为现代c++更新

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

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

但与此同时,14.6.4.2/1被更新,删除了链接(这来自c++ '14):

对于后缀表达式是依赖名称的函数调用,候选函数使用 通常的查找规则(3.4.1,3.4.2),除了: 对于使用非限定名称查找的部分(3.4.1),只找到模板定义上下文中的函数声明。 对于使用关联名称空间的查找部分(3.4.2),只会找到在模板定义上下文或模板实例化上下文中找到的函数声明。

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