5

Assume the following:

class A{ virtual void f() = 0; };
class B{ virtual void f() = 0; };

Can I do the following somehow?

class C : public A, public B
{
  virtual void A::f(){ printf("f() from A"); }
  virtual void B::f(){ printf("f() from B"); }
};

So now I can do???

A* pa = new C();
pa->f(); // prints f() from A;
B* pb = (B*)pa;
pb->f(); // prints f() from B;

Thanks!!!

William Pursell
  • 204,365
  • 48
  • 270
  • 300
TCS
  • 5,790
  • 5
  • 54
  • 86

4 Answers4

5

First solution

This question remind the 'facade' design pattern. This should be re-write as this :

class AC : public A
{ public: virtual void f(){ cout << "f() from A" << endl;}; };

class BC : public B ...

class C : public AC, public BC {};

where C is the 'facade'.

So in the correct calling syntax should be something like that :

C* c = new C();
c->AC::f();
c->BC::f();

If you don't have any share constraint between AC & BC this should do the job as it is'nt offuscated.

Second solution

Another solution, thank to Casey (see first comment), is to use a forward declaration of the class C in a template to allow calls to methods define latter.

template <typename C>
class AC : public A {
public:
    void f() { static_cast<C&>(*this).f_from_A(); }
};

template <typename C>
class BC : public B { ... };

so the implementation part can be done in the same class.

class C : public AC<C>, public BC<C> {
public:
    void f_from_A() { cout << "f_from_A" << endl; };
    void f_from_B() ...
};

The calling part is cleaner because it doesn't show any implementation details and it is closest to the question :

C* c = new C();
((A*) c) -> f();
((B*) c) -> f();

There is no more 'default' f() on C and it is possible to break the expected behavior of inheritance, and it is harder to read.

Galigator
  • 8,957
  • 2
  • 25
  • 39
  • 1
    If you remap the calls in a CRTP class, your implementations can actually be defined in `C` with full access to the object ([example at Coliru](http://coliru.stacked-crooked.com/a/1097abe9a3292dbf)). – Casey Dec 22 '13 at 17:41
0

For reference, the following links seem related:

  1. Overloading virtual functions of the same name from different base classes. Is it possible?
  2. C++ virtual override functions with same name
  3. Derived class defines function via base class

The OP in the second link asked if he could do something like you asked:

class Impl : public A , public B
{
  public:
     void A::Function () {  cout << "A::Function" << endl; }
     void B::Function () {  cout << "B::Function" << endl; }
};

Johannes answer is:

You cannot use qualified names there. I you write void Function() { ... } you are overriding both functions. Herb Sutter shows how it can be solved.

Another option is to rename those functions, because apparently they do something different (otherwise i don't see the problem of overriding both with identical behavior).

So one possible solution (by James Kanze from the first link) looks like this:

class RemapA : public A
{
    virtual void fnInA() = 0;
public:
    virtual void fn()
    {
        fnInA();
    }
};

class RemapB : public B
{
    virtual int fnInB() = 0;
public:
    virtual int fn()
    {
        return fnInB();
    }
};

class C : public RemapA, public RemapB
{
    virtual void fnInA() { /* ... */ }
    virtual void fnInB() { /* ... */ }
    //  ...
};

Herb Sutter's link essentially says the same thing.

Community
  • 1
  • 1
0

Yes. Each base class has its own virtual table.
So by casting, the compiler will check the virtual table of the type you're casting to, and get the pointer to the matching function there.

class A {public: virtual void f() = 0; };
class B {public: virtual void f() = 0; };

class C : public A, public B
{
public:
    virtual void A::f(){ printf("f() from A"); }
    virtual void B::f(){ printf("f() from B"); }
};

GCC doesn't support this. Only Visual Studio does.

But you will have to always use a cast or a scoped function call.
This will return an ambiguous call error:

C* c = new C();
c->f(); //ambiguous call compilation error

This will work:
((B*)c)->f();

The compiler shows the structure like this: (Visual studio flag: /d1reportSingleClassLayoutC)

  class C   size(8):
    +---
    | +--- (base class A)
   0    | | {vfptr}
    | +---
    | +--- (base class B)
   4    | | {vfptr}
    | +---
    +---

  C::$vftable@A@:
    | &C_meta
    |  0
   0    | &C::f

  C::$vftable@B@:
    | -4
   0    | &CCCC::f

  C::f this adjustor: 4
  C::f this adjustor: 0

Basically you can see the 2 separate pointers for the matching virtual tables.
So you could do:

    C* c= new C();
    B* b = (B*)c;
    b->f(); // calls C::B::f()
    A* a = (A*)c;
    a->f(); // calls C::A::f()

   or even:

    a = (A*)(C*)b; 
    a->f(); // calls C::A::f()

Note that you need to case b back to the derived class to access the virual table of the A type base class.

Yochai Timmer
  • 48,127
  • 24
  • 147
  • 185
  • This doesn't answer how `C` should override the two functions. They're abstract in the base classes. –  Dec 22 '13 at 15:24
  • @hvd I've added that part. – Yochai Timmer Dec 22 '13 at 15:28
  • @hvd and the "how" is just as he did it. – Yochai Timmer Dec 22 '13 at 15:30
  • 1
    You realise that the code in the question is pseudo-code, right? It doesn't get accepted by an actual C++ compiler. Or are you saying that MS's compiler does accept it? If so, that's an extension to C++, it's not standard, and it doesn't work on other compilers. –  Dec 22 '13 at 15:30
  • @hvd It's pretty much right. I've added the code in the beginning to avoid confusion. – Yochai Timmer Dec 22 '13 at 15:32
  • http://ideone.com/NTGElR error: cannot define member function ‘A::f’ within ‘C’ That's GCC's error message. clang's is similar. –  Dec 22 '13 at 15:34
  • @hvd right. but not everyone uses GCC. And it does work perfectly in visual studio. I'll add a remark – Yochai Timmer Dec 22 '13 at 16:09
  • 1
    I'm not assuming everyone uses GCC, I'm assuming C++ code posted here is standard C++ unless otherwise indicated. If it then doesn't work on GCC because of a bug in GCC, I don't consider that a problem in the answer. If it then doesn't work because the code is invalid, I do consider that a problem in the answer. Anyway, now that you have indicated that this is specific to MS's implementation: I doubt the OP is using Visual Studio: if the exact code in the question works for the OP, I don't think this question would have been asked. But the fact that it is accepted is interesting nonetheless. –  Dec 22 '13 at 16:15
0

For overriding both function you need intermediate classes. A using declaration in C selects the function of A:

#include <iostream>

struct A{ virtual void f() = 0; };
struct B{ virtual void f() = 0; };

struct IntermediateA : public A {
    virtual void f() override { std::cout << "A\n"; }
};

struct IntermediateB : public B {
    virtual void f() override { std::cout << "B\n"; }
};


struct C : public IntermediateA, public IntermediateB
{
    using IntermediateA::f;
};

int main() {
    C c;
    B* b = &c;

    c.f();
    b->f();
    return 0;
}