允许这种派生到基础的转换(当它似乎违反IS-A时)的基本原理是什么?

Ad *_*d N 4 c++ inheritance type-conversion

我用来快速确定派生到基础转换是否合法的规则是检查在转换的上下文中是否有derivedIS-A base(即,derived提供对公共API的访问base).
它在C++ Primer(第5版)中更好地表达为:

对于代码中的任何给定点,如果public可以访问基类的成员,则也可以访问派生到基础的转换,否则不可访问.

现在让我们假设一个类层次结构如下:

class Base
{
public:
    int mem;
};

class Derived : protected Base
{
    static void f(Derived *d)
    {
        d->mem; // OK, in this context, a Derived IS-A Base
        Base *b = d;
    }
};

int main()
{
    Derived d;
    //d.mem;        // Compilation error : in this context a Derived IS-NOT-A Base
    //Base *b = &d; // Compilation error too : consistent with the intuitive rule
    return 0;
}

class Derived_Derived : public Derived
{
    static void f(Derived *d)
    {
        //d->mem;    // Compilation error : in this context a Derived IS-NOT-A Base (as expected)
        Base *b = d; // COMPILATION OK : which seems to violate the rule above
    }
};
Run Code Online (Sandbox Code Playgroud)

似乎违反上述规则的结果是在类中Derived_Derived,函数f:编译器接受
转换db.在这种情况下,由于继承,不可能Base通过指向a的指针访问定义的公共API .然而,派生到基础的转换是有效的.Derivedprotected

它在一些编译器上进行了测试以得到相同的结果,因此我认为它是标准定义的行为.允许这样做的理由是什么?


编辑

Arne Vogel的回答证实它是一种标准定义的行为,具有相关的提取和解释.
然而,我找不到§11.2/ 4条件#3(授权最后一次转换)背后的基本原理,这通常有助于记住规则.

关于理由的问题仍然存在.

Arn*_*gel 5

C++ Primer的作者巧妙地错误地引用了ISO C++标准.引自N3376(C++ 11的后期/最终工作草案):

§11.2/ 4的基类BN是在可访问的[R ,如果

- 发明的公共成员B将是公共成员N,或

- - [R发生在成员或类的朋友N,以及一个发明了公共成员B将是私人或受保护的成员N,或

- R发生在P派生自一个类别的成员或朋友中N,并且发明的公共成员B将是私人或受保护的成员 P,或者

-存在一个类S,使得B是一个基类的S可访问的řS是一个基类的N可访问的- [R .

在您给出的示例中,第三个条件成立,基类可访问.继续...

5如果可以访问基类,则可以隐式地将指向派生类的指针转换为指向该基类的指针(4.10,4.11).

这意味着标准允许隐式转换,并且兼容的编译器必须支持它.

如果来自C++ Primer的文本是法律,那么基类转换应该同样被禁止访问,mem因为发明的公共成员不会被访问(参见§11.2/ 5,§11.4)mem.然而,这并不是标准所说的"发明的公共成员".在上述§11.2/ 4的条件#3中,它表示如果发明的公共成员是"P的私人或受保护成员",则可以访问基类,这显然就是这种情况.它不要求所述发明的私人或受保护成员实际可访问.