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

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

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

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


当前回答

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

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 :)

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

其他回答

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

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

根据我的经验,我只想指出,虽然这是c++将以前的静态函数放入匿名名称空间的方式,但旧的编译器有时会在这方面遇到问题。我目前正在使用一些用于目标平台的编译器,更现代的Linux编译器可以很好地将函数放置到匿名名称空间中。

但是在Solaris上运行的旧编译器有时会接受它,有时会将其标记为错误,直到未来未指定的版本发布。我担心的不是这个错误,而是它在接受错误时可能会做什么。因此,在全面现代化之前,我们仍然使用静态(通常是类作用域)函数,我们更喜欢匿名名称空间。

c++ 98标准不赞成为此目的使用static关键字。静态的问题在于它不适用于类型定义。它也是一个重载关键字,在不同的上下文中以不同的方式使用,因此未命名的名称空间简化了一些事情。

编译以下代码可以看出匿名名称空间和静态函数之间特定于编译器的区别。

#include <iostream>

namespace
{
    void unreferenced()
    {
        std::cout << "Unreferenced";
    }

    void referenced()
    {
        std::cout << "Referenced";
    }
}

static void static_unreferenced()
{
    std::cout << "Unreferenced";
}

static void static_referenced()
{
    std::cout << "Referenced";
}

int main()
{
    referenced();
    static_referenced();
    return 0;
}

使用VS 2017编译这段代码(指定4级警告标志/W4来启用警告C4505:未引用的本地函数已被删除)和gcc 4.9使用-Wunused-function或-Wall标志显示VS 2017只会对未使用的静态函数产生警告。GCC 4.9及更高版本,以及clang 3.3及更高版本,将对名称空间中未引用的函数产生警告,并对未使用的静态函数产生警告。

gcc 4.9和MSVC 2017的现场演示

在一种边缘情况下,静电会产生令人惊讶的效果(至少对我来说是这样)。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),只会找到在模板定义上下文或模板实例化上下文中找到的函数声明。

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