命名空间内的友元函数声明/定义

Friend function declaration/definition inside a namespace

本文关键字:声明 定义 函数 友元 命名空间      更新时间:2023-10-16

考虑一个名称空间中的类。类的定义声明了一个友元函数。

namespace Foo
{
    class Bar
    {
        friend void baz();
    };
}

根据我所知道的,这应该将baz()声明为最内层封闭命名空间的成员,即Foo

因此,我期望baz()的以下定义是正确的:

void Foo::baz() { }

但是,GCC(4.7)给了我一个错误。

error: ‘void Foo::baz()’ should have been declared inside ‘Foo’

有几个解决方案似乎有效:

  • 在类外声明baz()

    namespace Foo
    {
        void baz();
        class Bar
        {
            friend void baz();
        };
    }
    
  • 在命名空间内定义baz()

    namespace Foo
    {
        class Bar
        {
            friend void baz();
        };
    }
    ...
    namespace Foo
    {
        void baz() { }
    }
    
  • 使用-ffriend-injection标志编译,可以消除错误

这些解决方案似乎与我所知道的c++中声明/定义的一般规则不一致。

为什么我必须声明baz()两次?
为什么定义只在名称空间中合法,而在作用域解析操作符中不合法?
为什么这个标志消除了错误?

为什么我必须声明baz()两次?

因为友元声明没有在命名空间中提供可用的函数声明。它声明,如果该函数在该命名空间中声明,它将是友元;如果要在类中定义友元函数,则可以通过依赖参数的查找(而不是其他方式)获得它,就像在命名空间中声明一样。

为什么定义只在名称空间中合法,而在作用域解析操作符中不合法?

因为它没有(正确地)在名称空间中声明,并且如果函数已经声明,则只能在其名称空间之外定义(具有作用域解析)。

为什么这个标志消除了错误?

因为该标志使友元声明充当命名空间中的声明。这是为了与c++的古代方言(显然,还有一些现代编译器)兼容,在这些方言中,这是标准行为。

第一段代码应该这样编译:

namespace A {
   struct B {
      friend void foo();
   };
}
void A::foo() {}

尽管该特定函数不能使用,除非您还在命名空间级别提供声明。原因是友元声明只能通过参数依赖查找(ADL)看到,但foo不依赖于A::B,因此编译器永远不会查看该类型的内部。