3

Why is base inaccessible to deriv inside deriv? The program compiles with class deriv : public base.

#include <cstdio>

class base
{
};

class deriv : base
{
  public:
  void f(deriv, int){printf("deriv::f(deriv, int)\n");}
  void f(base){printf("deriv::f(base)\n");}
};

int main()
{
  deriv d;
  d.f(d);
}

17: error: ‘base’ is an inaccessible base of ‘deriv’
17: error:   initializing argument 1 of ‘void deriv::f(base)’

Because two people got it wrong already, I will ask in bold: why does base need to be publicly inherited? It is accessed from within deriv only.

Doru Georgescu
  • 309
  • 2
  • 12

3 Answers3

6

If you define a class with the class keyword, then members and base classes are private by default.

If the base class needs to be public, then either explicitly declare it public (as you mention in the question), or use the struct keyword which makes things public by default.

why does base need to be public?

In this case, your call to f(base) requires a conversion from deriv to base, and such a conversion is only possible if the base class is accessible. Being private, it is not accessible in main, which is where the conversion is required.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • Why does it need to be public? – Doru Georgescu Dec 20 '12 at 08:15
  • @DoruGeorgescu: I've updated the answer: conversion to a base class is only allowed if the base class is accessible. – Mike Seymour Dec 20 '12 at 08:18
  • @MikeSeymour: *"Being private, it is not accessible in main"*. I think the conversion is not possible anywhere, even in the member functions of either of the classes. – Nawaz Dec 20 '12 at 08:21
  • @DoruGeorgescu otherwise you can not access the base class (like you are trying to do) – BЈовић Dec 20 '12 at 08:22
  • 2
    @Nawaz: Why do you think that? Private members and base classes are accessible to members and friends; that's what "private" means. So the conversion is certainly possible in a member of `deriv`, as demonstrated [here](http://ideone.com/XrXs9E). – Mike Seymour Dec 20 '12 at 08:23
6

You seem to be incorrectly assuming that conversion from deriv to base when calling deriv::f(base) occurs "inside deriv" and thus has to be accessible. This is not the case. When you call a function, all conversions necessary for initializing function's arguments occur in the caller's context. They are not "inside derive". They happen in the "outside world". And in your case the "outside world" has no access to deriv-to-base conversion.

In your specific case it is main that is trying to convert deriv to base. And main cannot do it since it has no access to the private base of deriv. Just to experiment you can declare int main() as a friend of deriv and the code will compile.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Thank you for your answer. Am I right to suspect that this is a rather rough protection of privacy done by compiler? I understand that private class inheritance breaks the [Liskov substitution principle](http://en.wikipedia.org/wiki/Liskov_substitution_principle). This makes the outcome a bit counterintuitive. – Doru Georgescu Dec 20 '12 at 09:04
  • `main` does have access to `base`, but not to `base` through `deriv`. `deriv` does have access to `base`. very well, it is my fault that I used `private` inheritance ... :-) – Doru Georgescu Dec 20 '12 at 09:14
  • 1
    Trying to understand this behaviour, I imagined that the compiler treats privately inherited classes as it treats private members. This was not satisfactory for me, and it finally has dawned on me that the compiler takes a simple opportunistic approach: it regards access specifiers as "gateways". In main, a privately inherited base class is not accessible through conversion from a derived class. This also applies to pointers to and pointers to pointers to those classes, I believe. – Doru Georgescu Dec 21 '12 at 04:13
  • 1
    @Doru Georgescu: You are correct about pointers (and references), but "pointers to pointers" do not belong here. "Pointers to pointers" is a completely different story. `deriv **` is not convertible to `base **` even if `base` is a *public* base class. Also, base classes are indeed analogous to data members. Bases and data members are all *subobjects* of the complete object. The access syntax for bases is different from access syntax for data members, but in many other respects they are very similar. – AnT stands with Russia Dec 21 '12 at 04:38
  • Indeed, base classes are similar to data members, and the "gateway" rule applies to them, too. However, on the issue of deriv ** I hope that you are joking, right? Please tell me what happens, a link or at least a keyword ... – Doru Georgescu Dec 21 '12 at 09:53
  • Very well, I found these: [(1)](http://stackoverflow.com/questions/2532422/conversion-of-pointer-to-pointer-between-derived-and-base-classes) and [(2)](http://stackoverflow.com/questions/8026040/conversion-from-derived-to-base). In my opinion, polymorphism breaks encapsulation and too much of it ruins the OOP. That's the reason why `deriv **` can't be converted automatically to `base **`, and nobody wants to clearly state it. – Doru Georgescu Dec 21 '12 at 13:10
3

Because it is privately inherited :

class deriv : base

The default inheritance for class is private, meaning other classes and functions do not have access to the base class of the derived class.


There is a small problem in your example. This :

  deriv d;
  d.f(d);

is not going to do what you expect it to do because of slicing.

If we fix the above problem by changing the signature of the f to this :

void f(base&){printf("deriv::f(base)\n");}

there is still a problem accessing the base class of deriv, because it inherited privately from the base.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • @DoruGeorgescu In your specific example, it is `main` that can not convert `deriv` to `base`. Better wording might be needed :) – BЈовић Dec 20 '12 at 08:22