1

Is it good idea to replace virtual multiple inheritance (diamon) with teplates inheritence (linear)? For example I have this class diagram :

       IBase
    /          \
   /            \
IExtendedBase  BaseImpl
  \            /
   ExtendedImpl

I know that I can implement it with virtual inheritance. But can I use templates in order to make this diagram linear?

class IBase
    {
    public:
        virtual std::string name() = 0;
    };

    template<typename T>
    class BaseImpl : public T
    {
    public:
        virtual std::string name() override
        {
            return "BaseCommonImpl";
        }
    };

    template<typename T>
    class IExtendedBase : public T
    {
    public:
        virtual std::string extended_name() = 0;
    };

    template<typename T>
    class ExtendedBaseImpl : public T
    {
    public:
        virtual std::string extended_name() override
        {
            return "ExtendedBaseImpl";
        }
    };

Now with typedef I can specialize ExtendedBase

typedef IExtendedBase<BaseImpl<IBase>> _ExtendedBase;
typedef ExtendedBaseImpl<_ExtendedBase> _ExtendedBaseImpl;

Which method is better? Virtual inheritance or template inheritance?

ekarus303
  • 53
  • 1
  • 5
  • 4
    `Which method is better?` Both. Neither. It depends. – eerorika May 08 '15 at 11:33
  • In general extends as an inheritance approach should be avoided in order to eliminate the [fragile base class problem](http://en.wikipedia.org/wiki/Fragile_base_class) also see the book "Holub on Patterns" as Holub has some rather strong opinions on preferring implement rather than extends which make a lot of sense. Multiple inheritance just multiplies the problem. – Richard Chambers May 08 '15 at 11:33
  • It seems to me as if your question is not about *multiple* inheritance, but rather about static and dynamic inheritance in general (which are completely different approaches to obtain similar behaviour). In order to see that, try to set up a vector of base pointers ... it simply doesn't work for the static approach. Therefore, often a combination of both approaches is the most convenient choice: pack all those stuff independent of the derived type as virtual functions into the base class, and start with the static inheritance in a higher level of the hierarchy. – davidhigh May 08 '15 at 11:42
  • 1
    One significant distinction: with a non-template version you can polymorphically handle `ExtendedImpl` instances using `IExtendedBase*` or `IExtendedBase&`, or `IBase*` or `IBase&` as desired, whereas your templates mean a function just wanting to dispatch to `extended_name()` needs to take a parameter of type `IExtendedBase>*` or `&`, encoding/restricting the `BaseImpl` and making the function useless for any other class derived from `IExtendedBase` but not `BaseImpl`/`IBase`, unless the function itself is templated (with the usual potential for optimisation and bloat). – Tony Delroy May 08 '15 at 11:45
  • Consider using [type erasure](https://www.youtube.com/watch?v=0I0FD3N5cgM) instead. – nwp May 08 '15 at 11:50
  • You are asking an extremely broad question, with few objective parameters. This makes your question opinion based and/or overly vague. If you had a specific concrete issue or use case, and a set of concerns, it wouldn't be. Please examine the "how to ask" section of this website, and "what not to ask". – Yakk - Adam Nevraumont May 08 '15 at 13:49

2 Answers2

2

While you can obtain similar results using these two different approaches (multiple inheritance vs. template), there are important semantic differences.

Unfortunately, you do not give enough information to recommend an objective choice. So here some considerations:

Multiple inheritance approach

Multiple inheritance is meant for enforcing effective separation of concerns.

This approach should be preferred if in your case ExtendedImpl is-a IExtendBase and simulatneously is-a BaseImpl, but both inheritance relations are independent.

It has the inconvenience of a slight performance overhead in some cases (casting for example).

But it has the advantage of allowing your ExtendedImpl to be used where any of its bases could be used. And in addition, it allows for dynamic, runtime based polymorphism (if any of its base has a virtual member function).

enter image description here

Template approach

Templates are meant for generic programming. This approach is to be prefereed if your ExtendedImpl is really generic, and the "bases" are more parameters for your generic concept, rather than a concept that is extended further.

Template would have here the approach of a slightly better performance (single inheritance). But you don't implement the original concept of your initial schema. And you don't have the flexibility of dynamic polymorphism.

enter image description here

If the relation between the types would not be of generic nature, you might induce here undesired dependencies. For example here, IExtendedBase would inherit from BaseImpl. This could be ok in many cases. But it could be completely unnatural in other cases, leading to lasting desing issues in the maintenance phase.

Conclusion

Now it's up to you to decide which advantage and inconvenience fits best your specific case. If needed, you could edit your question giving more precise indication about the context and your intentions, and I'll adapt the answer accordingly.

Christophe
  • 68,716
  • 7
  • 72
  • 138
-1

This question is a request for opinions, so it will probably get closed down by a moderator.

Before it does, my advice would be to favour the approach that avoids multiple inheritance:

#include <iostream>
#include <string>

class IBase
{
public:
    virtual std::string name() = 0;
};


class IExtendedBase : public IBase
{
public:
    virtual std::string extended_name() = 0;
};


template<typename T>
class BaseImpl : public T
{
public:
    virtual std::string name() override
    {
        return "BaseCommonImpl";
    }
};

template<typename T>
class ExtendedBaseImpl : public T
{
    using inherited = T;
public:
    virtual std::string extended_name() override
    {
        return "ExtendedBaseImpl";
    }

    // optionally override name if you wish
    std::string name() override {
        return inherited::name() + "(extended)";
    }
};

typedef BaseImpl<IBase> Base;
typedef ExtendedBaseImpl<BaseImpl<IExtendedBase>> ExtendedBase;


using namespace std;

int main()
{

    Base a;
    a.name();
    cout << a.name() << endl;

    ExtendedBase b;
    cout << b.extended_name() << endl;
    cout << b.name() << endl;

}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142