基 class 的朋友 class 如何通过从基 class 派生的 class 的对象访问该基 class 的成员?

How does friend class of the base class access members of that base class through objects of class derived from the base class?

这是我在文件 source.cpp 中的代码:

class B
{
  friend class F;
protected:
  int protectedIntB;
};

class D : public B {};

class F
{
public:
  int f(D &d) {return ++d.protectedIntB;}
};

当我用 g++ -c -Wall -pedantic -std=c++11 source.cppcl /c source.cpp 编译上面的代码时,两个编译器都编译成功。但是,当我使用 protected 而不是 public:

使 D 从 B 继承时
class D : protected B {};

这次gcc编译成功,cl报错说B::protectedIntBreturn ++d.protectedIntB;.

中无法访问

另一种情况是用private替换public:

class D : private B {};

这一次,两个编译器都产生了错误。顺便说一句,我使用的是 mingw-w64 构建的 gcc 版本 5.3.0 和 VS2015 的 cl 版本 19.00.24210。

我的问题来了:

基 class 的朋友 class 如何通过从基 class 派生的 class 的对象访问该基 class 的成员,以及为什么 gcc 和 cl 处理方式不同?

编辑:

感谢 songyuanyao and Brian,在 protected 的情况下,这似乎是 gcc 5.3.0 中的一个错误。只有public的情况应该编译成功,gcc 6.1.0也可以正常运行。

如果代码在 gcc 5.3.0 上编译而不在 cl 上编译,则两者之一不严格执行 C++ 标准的可能性很高。如果我不得不猜测,对于受保护的和私有的继承,你应该得到一个编译器错误。有关不同类型继承之间的差异,请参阅 Difference between private, public, and protected inheritance 以获取更多详细信息。

为了class F能够访问class B的私有成员,应该知道class D派生自class B,它只会public 继承发生。

根据[class.access.base]/5:

The access to a member is affected by the class in which the member is named. This naming class is the class in which the member name was looked up and found.

根据[class.access.base]/6:

If a class member access operator, including an implicit “this->,” is used to access a non-static data member or non-static member function, the reference is ill-formed if the left operand (considered as a pointer in the “.” operator case) cannot be implicitly converted to a pointer to the naming class of the right operand.

因此,在您的特定情况下,为了访问 d.protectedIntB,以下两项都必须为真:

  • 您必须作为 B 的成员访问 protectedIntB,因为 B 是 class,其中名称 protectedIntB 被发现。 (注意:这可以通过使用 using-declaration 在派生的 class 中重新声明成员来改变; 在这种情况下派生的 class 将控制。)

  • 您必须能够访问 B 作为 D 的基础,即, 能够转换 D*B*。如果 BD 的 public 基数,没问题。如果 BD 的受保护基,则应用访问检查,F::f 失败,因为 F 不是 D 的友元,也不是派生的D 的 class。

令人惊讶的是,在受保护的情况下似乎 GCC 是错误的编译器但是这个错误 appears fixed. Note that Clang gives a much better diagnostic.

如果您使用保护或私有而不是 public 使 DB 继承,编译应该会失败。

根据标准,$11.2/5 base classes 和 base class 成员的可访问性 [class.access.base]:

A member m is accessible at the point R when named in class N if

(5.4) there exists a base class B of N that is accessible at R, and m is accessible at R when named in class B. [ Example:

class B;
class A {
private:
  int i;
  friend void f(B*);
};
class B : public A { };
void f(B* p) {
  p->i = 1;         // OK: B* can be implicitly converted to A*,
                    // and f has access to i in A
}

— end example ]

对于第一种情况,D 的基础 class B 可在 F::f() 访问,因为它是 public 继承。 B::protectedIntB 可以在 F::f() 访问,因为它是 class B.

的朋友

如果将其更改为受保护或私有继承,D 的基 class B 将无法再次在 F::f() 访问,那么编译应该会失败。注意 F::f() 不是派生 class D 的友元。这意味着如果你也将它设为 class D 的朋友,编译将成功。

顺便说一句:我用 gcc here 尝试了受保护的继承,但失败了。