来自 C++ Primer 第 5 版(D 继承自 B)



这有什么理由吗,还是我打算以面值来看待它?为什么会这样似乎很明显,但在一个例子中它让我绊倒了:

#include <iostream>
using namespace std;

class Base {
public:
    int x = 2;
};

class Derived : protected Base { };

class DerivedAgain : public Derived {
    friend void test();
};

void test() {
    ??? a;
    Base* p = &a;
    cout << p->x;
}

int main(){
    test();
}

我想了解 test() 对派生到基础转换中成员 x 的可访问性。考虑函数 ???atest() 类型的三种潜在情况。
  • ???BasexBase 的公共(public)成员。在这种情况下没有问题。
  • ???DerivedAgain 。在这种情况下,Derived-to-Base 转换是有意义的,因为 test() 具有 friendDerivedAgain 的所有成员的访问权限,包括那些间接继承自 Base 的成员。所以使用指针访问 x 没有问题。
  • ???Derived 。它编译得很好。但为什么?在这一点上我很困惑。 test()Derived 类的成员没有特殊的访问权限,那么为什么 p->x 应该工作,因此派生到基类的转换是有效的?它工作只是原因吗?

  • 确实,如果我将 test() 的操作更改为
    void test() {
        Derived a;
        cout << a.x;
    }
    

    它不会编译,正如我所期望的那样 - 因为 x 对象继承的 Derived 成员是 protected ,因此用户不能使用。

    如果我用 aBase 替换 DerivedAgain 的类型,修改后的 test() 编译得很好,正如我所期望的那样。

    我只是对为什么允许二级派生类的友元函数使用一级直接到基类的转换感到困惑,如果该友元函数没有对一级派生类成员的特殊访问权限。

    最佳答案

    基本上, protected 继承很奇怪。它编译的原因是,从 [class.access.base] 到 N4527:



    这里的第三个要点是相关的要点。 R 出现在派生自 test ( P ) 的类 DerivedAgain ( N ) 的友元 ( Derived ) 中,并且 B ( Base ) 的发明公共(public)成员将是 P ( DerivedAgain ) 的 protected 成员。

    我以前认为接受此代码是 gcc 错误( bug 67493 ),但现在我认为未能接受它是一个 clang 错误 - 尽管正如 T.C. 还指出的那样,存在相关的标准缺陷( CWG #1873 )。那里的措辞更改仅适用于成员访问,而此处与我们相关的是基本访问。但也许 gcc 只是在执行标准规则(接受是正确的),而 clang 遵循此缺陷报告(当前处于事件状态 CWG #472 )的逻辑结论,只是不允许它。

    同样, protected 继承真的很奇怪。欢迎来到 C++ 的奇妙世界。

    关于c++ - 派生到基础的转换和友元混淆,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33087785/

    10-13 09:29