2
votes

Here is the code:

class B;
class C;
class D;
class A{
    public:
        virtual ~A(){}
        friend void gg(D* d);
        void ga(B *b,C* c,D* d);
    };

class B:protected A{
    public:
        void gb(B *b,C* c,D* d);
};
class C:public B{};
class D:public C{};

void A::ga(B *b,C*c,D*d){
    A *a1=b;  // error: 'A' is an inaccessible base of 'B'
    A *a2=c;  // error: 'A' is an inaccessible base of 'C'
    A *a3=d;  // error: 'A' is an inaccessible base of 'D'
}
void B::gb(B *b,C*c,D*d){
    A *a1=b;  // no problem here 
    A *a2=c;  //why do classes derived from B can sucessfully convert to A here?
    A *a3=d;  //why do classes derived from B can sucessfully convert to A here?
}
void gg(D* d){
    A* a=d;
}
int main(){
    B b;
    C c;
    D d;
    A a;
    gg(&d);  // error: 'A' is an inaccessible base of 'D'
    a.ga(&b,&c,&d);
    b.gb(&b,&c,&d);
    A a1(d); //error here;Does it mean the implicit conversion in the user code is also user code?
    A a4=d;  //same as above
    return 0;
}

Here's the C++ primer said about accessibility of Derived-to-Base Conversion:

• User code may use the derived-to-base conversion only if D inherits publicly from B. User code may not use the conversion if D inherits from B using either protected or private.

• Member functions and friends of D can use the conversion to B regardless of how D inherits from B. The derived-to-base conversion to a direct base class is always accessible to members and friends of a derived class.

• Member functions and friends of classes derived from D may use the derived-to- base conversion if D inherits from B using either public or protected. Such code may not use the conversion if D inherits privately from B.

But it didn't talk about the conversion happen in B from C/D to A or conversion happen in A from B/C/D to A.I thought the first conversion would fail and second will success.But the result(see the comments in the code) surprises me.

Class A is the Base class of class B/C/D,so I thought Derive-to- Base conversion happened in A (in void A::ga(B*,C*,D*)) will success but the compiler complainted.From rule 2 of the reference from C++ primer I know conversion from B to A in void B::gb(B*,C*,D*) will sucess,but why conversion from C/D to A in void B::gb(B*,C*,D*) also success too? Why ?

2
Which result actually? - πάντα ῥεῖ
@πάνταῥεῖ the result is written in the comment of the code - Detective King
Then emphasize such in your question please, just in case dumbos like me reading it! ;) - πάντα ῥεῖ
@πάνταῥεῖ Ok,I've updated the post. - Detective King
And now explain please, what surprises you about these results in particular, and what you would have expected and why. - πάντα ῥεῖ

2 Answers

1
votes

That's the joy of accessibility :

But it didn't talk about the conversion happen in B from C/D to A

The inheritance is protected: this means that B is aware of the inheritance and can refer to its base class, and the classes derived from B also have this access.

Would B have inherited of A as private, the picture would be slightly different: in B all conversions would still succeeded (because B would have access to the private A base), but in functions of the derived classes all these conversions would fail because they would have no access to B's private life (and inheritance).

The justification is the standard :

  • (11.2/5) If a base class is accessible, one can implicitly convert a pointer to a derived class to a pointer to that base class
  • (11.2/1) If a class is declared to be a base class for another class using the protected access specifier, the public and protected members of the base class are accessible as protected members of the derived class. If a class is declared to be a base class for another class using the private access specifier, the public and protected members of the base class are accessible as private members of the derived class
  • (11.2/4) A base class B of N is accessible at R, if (...) R occurs in a member or friend of class N, and an invented public member of B would be a private or protected member of N.
  • (5.10/3) explains furhter that the conversion of derived to base is illegal if the base is not accessible.

conversion happen in A from B/C/D to A ... I thought ... success.

A is not a decendent of B. So it has no access to its protected and private members/inheritance.

0
votes

Here's the statement about accesss specifier in The C++ Programming Language

The access specifier for a base class controls the access to members of the base class and the conversion of pointers and references from the derived class type to the base class type. Consider a class D derived from a base class B:

• If B is a private base, its public and protected members can be used only by member functions and friends of D. Only friends and members of D can convert a D∗ to a B∗.

• If B is a protected base, its public and protected members can be used only by member functions and friends of D and by member functions and friends of classes derived from D. Only friends and members of D and friends and members of classes derived from D can convert a D∗ to a B∗.

• If B is a public base, its public members can be used by any function. In addition, its protected members can be used by members and friends of D and members and friends of classes derived from D. Any function can convert a D∗ to a B∗.

When Derive to Base conversion happens,the code where conversion happens must have all the accessibility from the derived class to base class through the inheritance hierarchy.

Given the example in the OP:

... //other same as before
void A::ga(B *b,C*c,D*d){
    A *a1=b;  // error: 'A' is an inaccessible base of 'B'
    A *a2=c;  // error: 'A' is an inaccessible base of 'C'
    A *a3=d;  // error: 'A' is an inaccessible base of 'D'  <<-- I'll explain this conversion 
}
...//other same as before

The conversion D->A happens in A, so A must have the accessibility of conversion D->C->B->A,we see D->C and C->B are public,but B->A is protected,this means D can be converted way through to B but will stop at B->A in A.To enable the conversion in A we must declare class A as friend in either class B or C or D by the protected rule.Then the compiler won't complain anymore.

Now what if class B derived from A and class C derived from B both inherited as private?

... // others same as before
class B:private A{ // inherited as private
    public:
        void gb(B *b,C* c,D* d);
};
class C:private B{};  // inherited as private 
class D:public C{};

void A::ga(B *b,C*c,D*d){
    A *a1=b; 
    A *a2=c;  
    A *a3=d;  //    <--------------------------- I'll explain this 
}
void B::gb(B *b,C*c,D*d){
    A *a1=b; 
    A *a2=c; 
    A *a3=d;  //   <============================ and this
}
...  // others same as before

In function A::ga, conversion D->C works,but fail in C->B and B->A, so we must declare class A as friend both in class B and C to get the accessibility.

In function B::gb,conversion D->C and B->A(by private rule) work, but C->B fail,so we must declare class B as friend in class C to achieve the D->A conversion in B.