-1

I have:

class A {
    virtual void foo() = 0;
    virtual void bar() = 0;
};

class Fooing {
    virtual void foo() = 0;
};

class Barring {
    virtual void bar() = 0;
};

class TallFooing : public Fooing {
    void foo(){ std::cout << "tall foo" << std::endl; }
};

class ShortFooing : public Fooing {
    void foo(){ std::cout << "short foo" << std::endl; }
};

class BlueBarring : public Barring {
    void bar() { std::cout << "blue bar" << std::endl; }
};

class RedBarring : public Barring {
    void bar() { std::cout << "red bar" << std::endl; }
};

template <typename S, typename T>
class B : public A, public S, public T { };

int main() {
    A* a1 = new B<TallFooing, RedBarring>();
    A* a2 = new B<ShortFooing, BlueBarring>();

    a1->foo(); a1->bar();
    a1->foo(); a2->bar();
};

Is mixing in the Barring classes a "Good Idea" (TM)? Am I doing it right? If not, can you suggest alternatives/improvements for doing the same?

Also, what about the following:

class Fooing {
    virtual void foo() = 0;
};

class Barring {
    virtual void bar() = 0;
};
class A : public virtual Fooing, public virtual Barring { };


class TallFooing : public Fooing {
    void foo(){ std::cout << "tall foo" << std::endl; }
};

class ShortFooing : public Fooing {
    void foo(){ std::cout << "short foo" << std::endl; }
};

class BlueBarring : public Barring {
    void bar() { std::cout << "blue bar" << std::endl; }
};

class RedBarring : public Barring {
    void bar() { std::cout << "red bar" << std::endl; }
};

template <typename S, typename T>
class B : public virtual A, public S, public T { };

and the same main()?

Notes:

  • foo() and bar() are, indeed, independent, and any combination goes. That is, nothing will break.
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • If A implemented `bar()`, the compiler would see a call like `a1->bar()` as ambiguous, because whose `bar()` did you really mean to call? This is the diamond problem, which you dance the line with using an approach like yours. – AndyG Aug 20 '14 at 13:33
  • @AndyG: note my edit. – einpoklum Aug 20 '14 at 13:33
  • my comment still stands. Also, I think you have a typo in `TallFooing` and `ShortFooing` (Should be `TallFoo` and `ShortFoo`, respectively) – AndyG Aug 20 '14 at 13:35
  • I would always try to prevent the usage of multiple inheritance. It shouldn't be used unless absolutely necessary. Instead you could simply construct the classes TallBlueBarring, TallRedBarring etc. In your example it would result in almost the same amount of code. – cageman Aug 20 '14 at 13:45
  • It depends how "independent" `foo` and `bar` really are. In some cases, you can mix and match policies. In others, it's absolutely wrong for a class implementation to use one version without a corresponding change in the other. – aschepler Aug 20 '14 at 13:46
  • 1
    People who tell you not to use multiple inheritance for interfaces like this, and then tell you to use interfaces in Java, can be safely ignored. Also, I don't see any diamonds in your inheritance. I do, however, see a class B which inherits two functions called foo() (one from A and one from S) which is confusing and may not even compile. You should avoid being confusing! – Andy Newman Aug 20 '14 at 13:47
  • @AndyG: Ok, _if_ `A` implemented `bar()`. But it expressly does not implement it... ; as for the typo - thanks, I've been edited :-) – einpoklum Aug 20 '14 at 13:51
  • @cageman: Why? can you link to some rationale? – einpoklum Aug 20 '14 at 13:53
  • @AndyNewman: Indeed... – einpoklum Aug 20 '14 at 13:58
  • Wait, doesn't `B` end up with two separate `foo()s` and two separate `bar()s`, through inheriting from both `A` and `S` as well as both `A` and `T`? Wouldn't you need to use virtual inheritance (assuming that's not what you want)? – NPE Aug 20 '14 at 13:58
  • 1
    It is not a good idea to write code that won't compile. Please come up with something that *can* be done, then ask whether it *should* be done. – n. m. could be an AI Aug 20 '14 at 14:00

2 Answers2

1

Your code does not compile (even after I fixed the syntax errors). The instantiations of B are abstract because they derive from A which has pure virtual functions and doesn't implement those. The function of same name from one parent doesn't implement a pure virtual function of another.

You could implement A in B with your template approach like this (I use struct for brevity, your code tries to access private members outside the class):

template<class S, class T>
struct B: A, S, T {
    void foo() {
        this->S::foo();
    }
    void bar() {
        this->T::bar();
    }
};

A perhaps more common approach would be to use virtual inheritance. It would probably make sense to define A in terms of the other interfaces:

struct A: virtual Fooing, virtual Barring {

Notice the use of virtual. This gives you the diamond which is mentioned in the comments. It is necessary in order for the compiler to make the connection between the foo's in separate interfaces. You must also inherit virtually in all the implementation classes:

struct TallFooing: virtual Fooing {

These changes should make your code compile. Whether multiple / virtual inheritance is a good idea is very much opinion based. Using it for anything other than pure virtual interfaces is often frowned upon.

Also, please make the destructor of the interface classes private or declare it virtual. Otherwise you'll probably end up with leaky code (compiler might hopefully notice that).

eerorika
  • 232,697
  • 12
  • 197
  • 326
0

I fixed all the compilation errors I could (missing include, missing public), but the child class B still doesn't inplement A::foo and A::bar so the program fails to compile even then. You just can't use multiple inheritance in C++ to implement an abstract interface in this way.

Mark B
  • 95,107
  • 10
  • 109
  • 188