6

Say I have 4 classes:

class I { public: virtual void X() = 0; };
class A : public virtual I { public: virtual void X() { } };
class B : public I {  };
class C : public A, public B { };

I, B and C are abstract, where as A is not. If I simply add virtual to the inheritance of I for B, then A::X() resolves I::X() in C.

However, I cannot change the source of B.

My question: Can I get A::X() to resolve I::X for C without being able to change B? I have tried declaring A and B to be virtual to C to no avail. I am trying to have no redundant code (e.g. have C declare X() { A::X(); }). Any neat hacks?

Also - there are a few questions very much like this, but I couldn't find any talking about using virtual inheritance. Please point to me one if I missed it.

i_am_jorf
  • 53,608
  • 15
  • 131
  • 222
payo
  • 4,501
  • 1
  • 24
  • 32

3 Answers3

3

Your problem is with the vtables. In your current code, you have two of them - one in A's I and one in B's I. As long as only A virtually inherits I, you could just as well use regular inheritance and save the overhead. If both virtually inherited I you'd have only one instance of I in C, therefore only one vtable, and A::X could indeed cover the pure virual I::X.

Given you can't change B, the only place you can take care of both vtables is C. In my opinion, the way to go is what you mention - just have C::X forward the call to A::X. There's no code duplication there, and it makes C non-abstract:

class C : public A, public B {
public:
    virtual void X() { A::X(); }
};

As for virtual inheritance, there definitively have been some here. But you're welcome to ask...

Community
  • 1
  • 1
Eran
  • 21,632
  • 6
  • 56
  • 89
  • I very much appreciate your explanation, thank you. Also, the 'smart' link of related questions had some gems as well. I'll be picking PiotrNycz answer - partially because the new guy could use some more reputation :) - and you both gave the answer I needed. Thank you again. – payo Jul 06 '12 at 18:37
2

This is quite good: When virtual inheritance IS a good design?

The problem here is that in C you have two interfaces I. That is why A::x() satisfies its interface I - but it cannot make not abstract interface I from class B. For C the only way to have exactly one interface of I - is to change B to derive from I virtually - in this way both I interfaces from A and from B will be merged to one in C. You cannot change B - so the only way is to add this redundant code which you are trying to avoid. I mean define C::X().

Community
  • 1
  • 1
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
  • I was hoping I didn't have to define C::X, but that does appear to be the consensus. Thank you. – payo Jul 06 '12 at 18:26
1

The only way I can think of is to compose a B* (or smart variant) into C instead of inheriting from it, and forward the appropriate methods. You can't do it while maintaining the inheritance, because the compiler won't know which I's inheritance chain to follow.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • interesting idea (point to "neat hack") I didn't mention in the question, but I need to expose C as a B*, so this won't work. But thank you for the suggestion. – payo Jul 06 '12 at 17:44
  • You can actually do this with inheritance from `B`. If you implement `X` in `C` it will serve as final overrider for `X` in both bases. – David Rodríguez - dribeas Jul 06 '12 at 17:47