3

Why the derived class Derived_from_Private is illegal? i noticed the member function has an reference to Base, but why it cannot have an reference to Base class?

class Base {
public:
  void pub_mem(); // public member
protected:
  int prot_mem; // protected member
private:
  char priv_mem; // private member
};

struct Pub_Derv : public Base {
  // legal
  void memfcn(Base &b) { b = *this; }
};

struct Priv_Derv : private Base {
  // legal
  void memfcn(Base &b) { b = *this; }
};

struct Prot_Derv : protected Base {
  // legal
  void memfcn(Base &b) { b = *this; }
};

struct Derived_from_Public : public Pub_Derv {
  // legal
  void memfcn(Base &b) { b = *this; }
};

struct Derived_from_Private : public Priv_Derv {
  // illegal
  void memfcn(Base &b) { b = *this; }
};

struct Derived_from_Protected : public Prot_Derv {
  // legal
  void memfcn(Base &b) { b = *this; }
};
Jing
  • 75
  • 3

4 Answers4

3

The expression

b = *this;

needs to invoke an implicit conversion from *this to an lvalue of type Base in order to call the implicitly declared Base::operator=(const Base&). This conversion goes through the path Derived_from_Private -> Priv_Derv -> Base. Since Priv_Derv has Base as a private base, Derived_from_Private does not have access to the second link.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • It has nothing to do with the assignment. the compiler will give me an error as long as i pass an Base as a parameter. – Jing Mar 02 '18 at 16:51
  • @JingleiRuan Ah, that's true. The answer xskxzr provided is correct. But you can work around it by using `::Base`. – Brian Bi Mar 02 '18 at 20:05
2

Priv_Derv inherits privately Base. This means that only the class itself knows that it's also a Base and only the member functions of Priv_Derv can use members of Base.

You can later let Derived_from_Private inherit publicly from Priv_Derv. It's legal. But unfortunately, due to the former private inheritance, it's as if Derived_from_Private doesn't have Base as base class.

Therefore your member function will fail to compile:

    void memfcn(Base &b) { b = *this; }

*this is a Derived_from_Private, but it's illegal to convert it to a Base class, because there is no known relation with that class due to the private inheritance.

Christophe
  • 68,716
  • 7
  • 72
  • 138
  • 1
    But i am not using members of Base. Even if i do not assign *this to Base, as long as i have a parameter of Base (reference or a copy) the compiler gives me an error. – Jing Mar 02 '18 at 09:44
  • @JingleiRuan interesting ! It seems that the private inheritance affects the unqualified name lookup. Use ::Base instead of Base to refer explicitly to the right (here global) namespace and it will compile. – Christophe Mar 02 '18 at 11:22
  • @JingleiRuan yes, that's it ! and it seems to be a compiler bug according to https://stackoverflow.com/a/41595470/3723423 But this compiler issue does not affect the validity of my initial answer. You'll see that the :: workaround will compile with an empty body, but fail as soon as you try the assignment. – Christophe Mar 02 '18 at 11:32
0

Inheritance can provide both subtyping and structural extension.

When you inherits privately from a base class you have no subtyping, only structural extension. Then (in your problematic case) when you write b = *this alas *this is not of the type Base because you have used private inheritance of it.

Private inheritance is usually used (that doesn't mean it's a good practice) to easily construct very simple composition (has a base, but not is a base).

Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
0

A name of a class is inserted into the scope of itself as a public member. This is so-called injected-class-name. Name lookup for Base in the derived class Derived_from_Private will find its injected-class-name instead of the normal one. Because the injected-class-name of Base is treated as a public member of Base, thus is treated as a private number of Priv_Derv, it is inaccessible in Derived_from_Private.

Quoted from [class.access.spec] paragraph 5:

[ Note: In a derived class, the lookup of a base class name will find the injected-class-name instead of the name of the base class in the scope in which it was declared. The injected-class-name might be less accessible than the name of the base class in the scope in which it was declared. — end note ] [ Example:

class A { };
class B : private A { };
class C : public B {
  A* p;             // error: injected-class-name A is inaccessible
  ::A* q;           // OK
};

— end example ]

xskxzr
  • 12,442
  • 12
  • 37
  • 77