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

79,968

Solution 1

By making the inheritance private, you're basically saying that even the fact that B inherits from A (at all) is private -- not accessible/visible to the outside world.

Without getting into a long-winded discussion of what would happen if it was allowed, the simple fact is that it's not allowed. If you want to use a pointer to base to refer to an object of derived type, then you're pretty much stuck with using public inheritance.

Private inheritance is not necessarily (or even normally) intended to follow the Liskov substitution principle. Public inheritance asserts that a derived object can be substituted for an object of the base class, and proper semantics will still result. Private inheritance does not assert that though. The usual description of the relationship implied by private inheritance is "is implemented in terms of".

Public inheritance means a derived class maintains all the capabilities of the base class and potentially adds more besides. Private inheritance often means more or less the opposite: that the derived class uses a general base class to implement something with a more restricted interface.

Just for example, let's assume for the moment that the containers in the C++ standard library were implemented using inheritance rather than templates. In the current system, std::deque and std::vector are containers, and std::stack is a container adapter that provides a more restricted interface. Since it is based on templates, you can use std::stack as an adapter for either std::deque or std::vector.

If we wanted to provide essentially the same with inheritance, we would probably use private inheritance, so std::stack would be something like:

class stack : private vector {
    // ...
};

In this case, we definitely do not want the user to be able to manipulate our stack as if it were a vector. Doing so could (and likely would) violate the expectations of a stack (e.g., the user could insert/remove items in the middle, rather than a purely stack-like fashion as intended). We're basically using vector as a convenient way to implement our stack, but if (for example) we changed the implementation for stack stand alone (with no dependence on a base class) or re-implement it in terms of std::deque, we do not want that to affect any client code -- to the client code, this is supposed to be just a stack, not some specialized variety of vector (or deque).

Solution 2

private inheritance should only change how the members of class B are visible to the outside world

It does. And if

A* p = new B;

were allowed, then the inherited members of any B could be accessed from the outside world, just by making a A*. Since they're privately inherited, that access is illegal, and so is the upcast.

Solution 3

clang++ gives a slightly more understandable error message:

example.cpp:9:13: error: cannot cast 'B' to its private base class 'A'
    A* ab = new B;
            ^
example.cpp:6:11: note: declared private here
class B : private A { };
          ^~~~~~~~~
1 error generated.

I'm not a C++ expert, but it looks like it's just simply not allowed. I'll go poke around the spec and see what I come up with.

Edit: here's the relevant reference from the spec - Section 4.10 Pointer conversions, paragraph 3:

A prvalue of type "pointer to cv D", where D is a class type, can be converted to a prvalue of type "pointer to cv B", where B is a base class of D. If B is an inaccessible or ambiguous base class of D, a program that necessitates this conversion is ill-formed.

Solution 4

It's pretty simple: the fact that A is inherited privately means that the fact that B extends A is a secret, and only B "knows" it. That is the very definition of private inheritance.

Solution 5

Private inheritance means that outside the derived class, the inheritance information is hidden. That means you can't cast the derived class to the base class: the relationship isn't known to the caller.

Share:
79,968
Lazer
Author by

Lazer

Updated on July 08, 2022

Comments

  • Lazer
    Lazer about 2 years
    $ cat inheritance.cpp 
    #include <iostream>
    
    using namespace std;
    
    class A { };
    class B : private A { };
    
    int main() {
        A* ab = new B;
    }
    $
    $ g++ inheritance.cpp
    inheritance.cpp: In function 'int main()':
    inheritance.cpp:9: error: 'A' is an inaccessible base of 'B'
    $
    

    I just do not understand this error.

    As I understand, and as this tutorial confirms, private inheritance should only change how the members of class B are visible to the outside world.

    I think the private specifier is doing more than just change visibility of class B members here.

    • Why do I get this error when inheriting and what does it mean?
    • Basically what is wrong with allowing this type of code in C++? Looks totally harmless.
  • Lazer
    Lazer over 12 years
    But I get the very same error if I replace private with protected.
  • Ernest Friedman-Hill
    Ernest Friedman-Hill over 12 years
    Indeed. "Protected" means that the knowledge is limited to B and subclasses (and friends) of B. "A* ab = new B;" would be legal in a hypothetical class C that was a subclass of B.
  • Lazer
    Lazer over 12 years
    Thanks, but it somehow doesn't make sense. privates only business should be to control how the members behave. What would be the harm if the inheritance information is not hidden?
  • tmpearce
    tmpearce over 12 years
    Private inheritance is a form of aggregation/composition. It's a way to have the properties of a base class, without being an object of the base class. If that isn't what you want, private inheritance isn't for you. That's just how it works.
  • SubMachine
    SubMachine over 4 years
    this also applies to protected
  • Keshav Sahu
    Keshav Sahu over 2 years
    But we can create getters that returns this as pointer to base class.
  • Jerry Coffin
    Jerry Coffin over 2 years
    @KeshavSahu: Oh, of course. As the old saying goes, C++ tries to protect against Murphy, not Machiavelli. In other words, it tries to protect you from accidents, but if you write the code to bypass its protection, it won't try to stop you.