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

5

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

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

在另一个编译单元中,我想定义bar。 根据我使用的编译器,我需要将bar放在命名空间NS或全局命名空间中才能连接:
在clang上:
namespace NS
{
    void bar() {}
}

在MSVC中:

void bar() {}

如果有的话,什么是好的行为?

另外一个问题是,为什么以下代码在我的两个编译器上都无法编译:

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个回答

3

注意:长话短说,msvc做错了,clang是正确的。本文将通过展示相关代码片段和标准引用来支持这些观点,解释为什么。

关于第一个代码片段/问题


介绍

在块作用域中的函数声明被认为是指向最内层封闭命名空间中的实体,所以在下面的例子中,bar指的是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声明的变量名称具有链接性。如果存在一个具有相同名称和类型的链接实体的可见声明,忽略在最内层命名空间范围之外声明的实体,则块作用域声明将声明相同的实体并接收先前声明的链接。 如果有多个这样的匹配实体,则程序是非法的。否则,如果找不到匹配的实体,则块作用域实体接收外部链接。


结论

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

感谢您的回答。关于第二个问题,我已经修复了最后一个例子。如果bar在命名空间NS中声明,我想知道为什么会出现“错误:命名空间'NS'中没有名为'bar'的成员”的错误提示。 - RTempete
@user3744088,请查看更新的帖子以获取进一步说明。 - Filip Roséen - refp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接