3

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

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()两次?
为什么定义仅在命名空间内是合法的,而在范围解析运算符中是非法的?
为什么标志消除了错误?

4

2 回答 2

6

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

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

为什么定义仅在命名空间内是合法的,而在范围解析运算符中是非法的?

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

为什么标志消除了错误?

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

于 2013-05-23T15:51:25.047 回答
2

第一段代码应该编译:

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

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

于 2013-05-23T15:51:59.393 回答