3

I have two classes A and B, both defining method m(). Although the signatures of these methods are the same, they are two totally different methods with different meanings. In class C, I want to update both m(). Instead of writing a new m() in class C that fuses the two m() from A and B, I want to override them separately. See the following code:

class A { public: virtual void m() {cout << "MA" << endl;}};
class B { public: virtual void m() {cout << "MB" << endl;}};
class C : public A, public B { 
   public: virtual void m() update A {cout << "MA2" << endl;}};
   public: virtual void m() update B {cout << "MB2" << endl;}};
}
void func(A* a) { a->m(); }
int main() {
    C* c = new C();
    c->B::m();       //print "MB2"
    func(c);         //print "MA2"
    return 0;
}

I know that the update syntax is not supported in C++, but is there any workaround to simulate this in C++?

Mel
  • 5,837
  • 10
  • 37
  • 42
Grace Wang
  • 39
  • 1
  • Judging from [this Q&A](https://stackoverflow.com/questions/18398409/c-inherit-from-multiple-base-classes-with-the-same-virtual-function-name) looks like you are out of luck. Not sure if it should be closed as a dupe though. – NathanOliver Jun 19 '17 at 13:52
  • `c->B::m();` will **always** call `B::m()` without dynamic dispatch. Your example should instead cast to `B*`. – StoryTeller - Unslander Monica Jun 19 '17 at 14:12
  • Yes you're right, in C++, :: is static lookup. However, even if I use "B*", I guess there's still no way in C++ to define method "update" as I described? – Grace Wang Jun 26 '17 at 14:55

2 Answers2

3

A nasty part of multiple inheritance is that indeed there is only one m in C that overrides both. You need a middle-man class to disambiguate the call in case of 2 or more classes that define m. Fortunately, you can use a template in order to automate the process:

template<class C>
class MiddleMan : public C {
  void m() override final { m_impl(this); }
protected:
  virtual void m_impl(MiddleMan*) = 0;
};

Now you just use this template and override the new functions it adds:

class C : public MiddleMan<A>, public MiddleMan<B> { 
   private:
     void m_impl (MiddleMan<A>*) override {cout << "MA2" << endl;}
     void m_impl (MiddleMan<B>*) override {cout << "MA2" << endl;}
};

The implementation of m in each middle man class will call the new virtual functions that are overloaded. You then define these overloads. I used MiddleMan* as the extra parameter which facilitates overloading, but any type which is cheap to pass by value and depends on the template parameter will do for disambiguation.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
0

You just cannot do that, for the same reason in C you can't have two functions with the same name: the compiler would not know which one to call! To get the same effect that you want, I'd change your code to look as follows:

class A { public: virtual void ma() {cout << "MA" << endl;}};
class B { public: virtual void mb() {cout << "MB" << endl;}};
class C : public A, public B { 
   public: virtual void ma() override {cout << "MA2" << endl;} // these are *extra*: };
   public: virtual void mb() override {cout << "MB2" << endl;} // these are *extra*: };
}
void func(A* a) { a->ma(); } // the problem here with a->m() would be 
// that the compiler would not know which m() function to call --
// you basically want two pointers *crammed* somehow in the same vfptr entry,
// and the compiler to *automagically* know which one to call,
// depending on... unspecified conditions!
int main() {
    C* c = new C();
    c->B::m();       //this will never print "MB2" in your code;
                     //it will always print "MB", no matter what you do!
    c->mb();         //print "MB2"
    func(c);         //print "MA2"
    return 0;
}

I know, this may not be the answer you're looking for, but then you have to describe your problem better. In my opinion, this is a valid solution to your problem, as it's currently described.