嵌套在函数中的函数声明的命名空间

时间:2014-06-16 08:53:09

标签: c++ namespaces language-lawyer forward-declaration

由于奇怪的原因,我想在函数范围内声明一个函数。 所以我得到以下代码:

namespace NS
{
    void foo()
    {
        void bar();
        bar();
    }
}

在另一个编译单元中,我想定义bar。 根据我正在使用的编译器,我需要将bar放在命名空间NS或全局命名空间中以便能够链接:

On clang:

namespace NS
{
    void bar() {}
}

在MSVC上:

void bar() {}

如果有什么好的行为?

作为一个附带问题,为什么以下代码都没有编译(在我的2个编译器上):

namespace NS
{
    void foo()
    {
        void bar();
        ::bar(); // bar declared in global namespace
    }
}

namespace NS
{
    void foo()
    {
        void bar();
        ::NS::bar(); // bar declared in NS namespace
    }
}

感谢您的帮助。

GiSylbe

1 个答案:

答案 0 :(得分:1)

  

注意:长篇故事,简短; msvc 做错了, clang 是正确的。这篇文章将解释为什么通过显示相关的片段,并使用标准报价来支持声明。

关于第一个片段/问题


简介

块作用域中的函数声明被称为最内层封闭命名空间中的实体,因此在下面的栏中指的是 NS :: bar

namespace NS {
  void foo () {
    void bar (); // (A), forward-declaration of `NS::bar`

    bar ();      // call (A)
  }
}

这意味着,在定义 bar 时,您必须在命名空间 N 中执行此操作,或者使用 qualified-id 进行引用到实体。

void f ():     // some other `void f()`;  _not_ (A)

void NS::f (); // same `f` as being declared at (A)

namespace NS {
  void f ();   // same `f` as being declared at (A)
}

标准说什么? (n3337

  

3.5p6 计划和关联 [basic.link]

     
    

块作用域中声明的函数的名称和块作用域extern声明声明的变量的名称具有链接。如果存在具有相同名称和类型的链接的enity的可见声明,忽略在最内部封闭命名空间范围之外声明的实体,则块范围声明声明相同的权限并接收先前声明的链接。如果存在多个这样的匹配实体,则该程序是不正确的。否则,如果未找到匹配的实体,则块范围实体将接收外部链接。

  

结论

msvc做错了,clang显示的行为是正确的。



关于第二个片段/问题


解释

当遇到块范围声明时,它指的是最近的封闭命名空间中的实体,但在该命名空间中引入此类名称。

  

3.5p7 计划和关联 [basic.link]

     
    

当找不到具有链接的实体的块范围声明来引用其他声明时,该实体是最内层封闭命名空间的成员。但是,这样的声明不会在其命名空间范围内引入成员名称。

  

见下面的例子:

namespace NS {
  void foo() {
    void bar();  // (A), forward-declaration of `NS::bar`
    ::NS::bar(); //      ill-formed, there is no `::NS::bar` yet
    bar ();      //           legal, call the `bar` being forward-declared by (A)
  }
}

在上面(A)中引用即将发布的::NS::bar声明,但由于前向声明不使名称空间NS 具有名为 bar 我们只能通过 bar 来引用将被称为::NS::bar的实体。

void bar ();    // (A), `::bar`

namespace NS {
  void foo() {
    void bar();   // (B), forward-declaration of `NS::bar`

    ::bar();      // call (A)
    bar ();       // call (B), `NS::bar`

    ::NS::bar (); // ill-formed, `namespace NS` doesn't have an entity named bar (yet) 
  }

  void bar ();    // (C), make `NS` have an entity named bar, this is the one
                  //      being referred to by (B)

  void baz () {
    ::NS::bar (); // legal, `namespace NS` now have an entity named `bar`, as being
                  //        declared in (C)
  }
}