Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

error: 'A' is an inaccessible base of 'B'

I've a code as follows -

#include <iostream>
#include <string>

class A{
    int a;
    public: virtual void sayHello(){ std::cout << "Hello\n"; }
};

class B : private A{
    std::string name;
  public:
    B(std::string _n): name(_n){}
    void sayName(){std::cout << name << "says hello\n";}
    void sayHello(){sayName();}
};


int main() {
    A *ptr = new B("c++");
    ptr->sayHello();
    return 0;
}

which produces the following compiler output -

Error:

prog.cpp: In function 'int main()':
prog.cpp:20:22: error: 'A' is an inaccessible base of 'B'
  A *ptr = new B("c++");
                      ^

As previously answered - here, here & here, I know how to solve this issue. By using public inheritence instead of private or protected.

But if I really really want to hide some interface behind the base class, isn't there some other way to do this? Or is it impossible to do so according to c++ lang specification.

like image 815
Abhinav Gauniyal Avatar asked Sep 05 '16 14:09

Abhinav Gauniyal


3 Answers

If you want polymorphic pointer conversion to work outside the class, then the inheritance must be public. There is no way to work around that.

You could add a member function that does the polymorphic pointer conversion within the class:

class B : private A{
    // ...
public:
    A* getA() {
        return this;
    }
};

Which allows you to do this, while still allowing private inheritance:

B* b_ptr = new B("c++");
A* ptr   = b_ptr->getA();
// ptr   = b_ptr; // only allowed in member functions

I haven't encountered a real world design where this trick would be useful, but suit yourself.


PS. Remember that you should destroy objects that you create. Also do realize that delete ptr has undefined behaviour, unless ~A is virtual.

like image 73
eerorika Avatar answered Nov 03 '22 13:11

eerorika


Even though I find it quite strange to hide the base class and want to cast B to A, you can use for that the operator A*().
It follows a minimal, working example:

#include <iostream>
#include <string>

class A{
    int a;
public:
    virtual void sayHello(){ std::cout << "Hello\n"; }
};

class B : private A{
    std::string name;
public:
    B(std::string _n): name(_n){}
    operator A*() { return this; }
    void sayName(){std::cout << name << "says hello\n";}
    void sayHello(){sayName();}
};

Now you can use it as:

int main() {
    A *ptr = *(new B("c++"));
    ptr->sayHello();
    return 0;
}

Or even better:

int main() {
    B b{"c++"};
    A *ptr = b;
    ptr->sayHello();
    return 0;
}

Adding the cast to A& is as easy as adding the member method operator A&() defined as return *this;.

like image 27
skypjack Avatar answered Nov 03 '22 14:11

skypjack


There is an unsightly way around this: C style casts. C style casts can cast to inaccessible base classes. This is the one and only case where C style casts can do something that C++ casts can't. From cppreference, when a C style cast (T) foo attempts to perform static_cast<T>(foo), it can do slightly more than just a static_cast:

[P]ointer or reference to a derived class is additionally allowed to be cast to pointer or reference to unambiguous base class (and vice versa) even if the base class is inaccessible (that is, this cast ignores the private inheritance specifier).

Emphasis added

Thus, you can do this:

int main() {
    A *ptr = (A *) new B("c++");
    ptr->sayHello();
    return 0;
}

This is ugly, and it comes with all the disadvantages of casting and especially of C style casts. But it does work, and it is allowed.

Live on Wandbox

like image 42
Justin Avatar answered Nov 03 '22 13:11

Justin