为啥 C++ 友元类只需要在其他命名空间中进行前向声明?

Posted

技术标签:

【中文标题】为啥 C++ 友元类只需要在其他命名空间中进行前向声明?【英文标题】:Why does a C++ friend class need a forward declaration only in other namespaces?为什么 C++ 友元类只需要在其他命名空间中进行前向声明? 【发布时间】:2011-05-28 09:12:04 【问题描述】:

假设我有一个类 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;

在我看来,这似乎不一致。这是有原因的还是只是标准的设计决定?

【问题讨论】:

【参考方案1】:

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

您的friend class BF;A::BF 在命名空间A 而非全局命名空间中的声明。您需要全局先验声明来避免这个新声明。

【讨论】:

一个理性的人可能会问为什么写“friend class ::F;”(如 OP 的代码所示)是不够的,从而显式地将F 推入全局命名空间。我认为答案是“一个合格的 id 永远不会声明一个新名称”,但我不确定标准在这一点上到底说了什么。 @Zack 这也是我最初的问题:当我在另一个命名空间中移动一个类时,为什么需要添加前向声明?但是已经有一些关于这个主题的讨论(例如***.com/questions/2059665、***.com/questions/1368642)。似乎不需要在同一范围内进行前向声明是标准作者授予的便利。【参考方案2】:

让我们考虑一下示例中的这 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.

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

友元声明本身不会使名称对 不合格的查找 (3.4.1) 或合格的查找 (3.4.3)。 [注: 如果匹配的朋友的名字将在其命名空间中可见 声明是在命名空间范围内提供的(在 授予友谊的类定义)。 ——尾注]

第 2 行完全符合标准的要求。

所有的困惑都是因为“朋友声明”是,您需要提供可靠的前向声明以供进一步使用。

【讨论】:

【参考方案3】:

因为如果您在 namespace 块内,那么在全局命名空间中声明某些内容是没有意义的。 friend class BF; 起作用的原因是它的行为类似于隐式前向声明。

【讨论】:

以上是关于为啥 C++ 友元类只需要在其他命名空间中进行前向声明?的主要内容,如果未能解决你的问题,请参考以下文章

C++之:友元类

C++ 友元类,友元函数

C++友元类

C++友元类

C++友元类

C++ 友元函数 友元类 friend class