为什么C++好友类只需要在其他命名空间中使用前向声明?

pes*_*che 46 c++ namespaces friend forward-declaration

假设我有一个类F应该是类G(在全局命名空间中)和C(在命名空间中A)的朋友.

  • 要成为朋友A::C,F必须向前宣布.
  • 要成为朋友G,没有F必要的前瞻性声明.
  • 同样,一个班级A::BF可以成为朋友而A::C无需前瞻性声明

下面的代码说明了这一点,并使用GCC 4.5,VC++ 10以及至少与另一个编译器进行编译.

class G {
    friend class F;
    int g;
};

// without this forward declaration, F can't be friend to A::C
class F;

namespace A {

class C {
    friend class ::F;
    friend class BF;
    int c;
};

class BF {
public:
    BF() { c.c = 2; }
private:
    C c;
};

} // namespace A

class F {
public:
    F() { g.g = 3; c.c = 2; }
private:
    G g;
    A::C c;
};

int main()
{
    F f;
}
Run Code Online (Sandbox Code Playgroud)

这对我来说似乎不一致.这是有原因还是只是标准的设计决定?

Ale*_*tov 45

C++ 标准 ISO/IEC 14882:2003(E)

7.3.1.2命名空间成员定义

第3段

首先在名称空间中声明的每个名称都是该名称空间的成员.如果非本地类中的友元声明首先声明一个类或函数(这意味着该类或函数的名称是不合格的),那么友元类或函数是最内层封闭命名空间的成员.

// Assume f and g have not yet been defined.
void h(int);
template <class T> void f2(T);
namespace A {
   class X {
   friend void f(X);  //  A::f(X) is a friend
      class Y {
         friend void g();  //  A::g is a friend
         friend void h(int);  //  A::h is a friend
         //  ::h not considered
         friend void f2<>(int);  //  ::f2<>(int) is a friend
      };
   };
   //  A::f, A::g and A::h are not visible here
   X x;
   void g() { f(x); }  // definition of A::g
   void f(X) { /* ... */}  // definition of A::f
   void h(int) { /* ... */ }  // definition of A::h
   //  A::f, A::g and A::h are visible here and known to be friends
}
Run Code Online (Sandbox Code Playgroud)

friend class BF;A::BF名称空间A而不是全局名称空间的声明.您需要全局事先声明才能避免此新声明.

  • 一个理性的人可能会问为什么写"朋友类:: F;`"(如OP的代码所示)是不够的,因此明确地将`F`推入全局命名空间.我认为答案是"有资格的id永远不会宣布一个新名字",但我不确定标准在这一点上究竟是什么. (13认同)

Loo*_*pes 6

让我们从您的示例中考虑以下3个代码行:

1. friend class F; // it creates "friend declaration", (that's not the same as ordinary forward declaration

2. class F; // without this forward declaration, F can't be friend to A::C <-- this is ordinary forward declaration

3. friend class ::F; // this is qualified lookup (because of ::), so it needs previous declaration, which you provide in line 2.
Run Code Online (Sandbox Code Playgroud)

第7.3.1.2节中的C++标准,第3点(命名空间成员定义)说:

友元声明本身不会使名称对非限定查找(3.4.1)或限定查找(3.4.3)可见.[注意:如果在命名空间范围内提供匹配的声明(在类定义授予友谊之前或之后),则朋友的名称将在其命名空间中可见. - 尾注]

第2行完全符合标准要求.

所有的困惑都是因为"朋友宣言" 很弱,你需要提供坚实的前瞻性声明以供进一步使用.