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

Namespace of a function declaration nested in function

本文关键字:函数 声明 命名空间 嵌套      更新时间:2023-10-16

出于奇怪的原因,我想在函数作用域中声明一个函数。所以我得到以下代码:

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

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

在叮当声

:

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

注释:长话短说;msvc是错误的,而clang是正确的。这篇文章将通过展示相关的片段来解释为什么,并有标准的引用来支持这些说法。

关于第一个片段/问题


介绍

块范围内的函数声明被称为引用最内层封闭命名空间中的实体,因此在下面的bar中引用NS::bar

namespace NS {
  void foo () {
    void bar (); // (A), forward-declaration of `NS::bar`
    bar ();      // call (A)
  }
}
这意味着在定义时,必须在名称空间N中这样做,或者使用限定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声明的变量名有关联。如果存在具有相同名称和类型的链接的实体的可见声明,忽略在最内层封闭命名空间作用域之外声明的实体,则块作用域声明声明相同的实体并接收前一个声明的链接。如果有多个这样的匹配实体,则程序是病态的。否则,如果没有找到匹配的实体,则块作用域实体接收外部链接。


结论

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