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

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

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

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


当前回答

此外,如果在变量上使用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),只会找到在模板定义上下文或模板实例化上下文中找到的函数声明。

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

我最近开始在代码中用匿名名称空间替换静态关键字,但很快就遇到了一个问题,即名称空间中的变量在调试器中不再可用。我使用的是VC60,所以我不知道其他调试器是否没有问题。我的解决方法是定义一个“模块”命名空间,在那里我给它我的cpp文件的名字。

例如,在我的xmlutil_cpp文件中,我定义了一个名称空间XmlUtil_I{…}用于我的所有模块变量和函数。这样我就可以在调试器中应用XmlUtil_I::资格来访问变量。在本例中,_I将其与公共名称空间区分开来,例如我可能希望在其他地方使用的XmlUtil。

我认为,与真正的匿名方法相比,这种方法的潜在缺点是,有人可能会在其他模块中使用名称空间限定符,从而违反所需的静态作用域。我不知道这是否是一个主要的问题。

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

#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的现场演示

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

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

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

编辑:

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

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

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