Reputation: 2193
I have an inheritance chain with Base being the base class. I want to be able to write a class template which inherits Base and possible another Base-derived class. I could use virtual inheritance, but I found another solution. I'd like to know if it's common/considerable/legitimate class design:
Write a class template in which the template parameter is the class it derives from, i.e. it has to be Base or a Base-derived class. In the constructor I can use static assert to really make sure the user didn't use any illegal class as the template parameter.
If it works, I won't ever have virtual inheritance problems... the question is, it it ok to do that. I never saw it in other projects, so I want to make sure before I use it.
EDIT: Just to be sure I don't confuse you, here's some code:
class Base
{
};
class Derived : public Base
{
};
template <Class TheBase>
class MyDerived : public TheBase
{
};
Now I can use Base
or any Base
-derived class, e.g. Derived
, as the TheBase
parameter.
Upvotes: 14
Views: 8662
Reputation: 8203
This is a valid design pattern. It is mixin inheritance, not CRTP. Mixin inheritance provides a way to simulate multiple inheritance safely by the programmer manually linearizing the inheritance hierarchy. The templated classes are the mixins. If you want to extend a class with multiple mixins you have to decide the order of the composition like Big<Friendly<Dog> >
.
Mixin programming in C++ is described in this Dr Dobb's article.
Mixins can be used to implement a static version the GoF Decorator pattern as described here.
Mixins play a simlar role in C++ that traits (not C++ traits) play in Scala & SmallTalk.
In CRTP it is the base class that is a template:
template <class Param>
class Base { ... };
class Derived : public Base<Derived> { ... };
Upvotes: 23
Reputation: 1639
Later edit: One year later, here I am revising my own answer. I initially erroneously stated that the pattern OP posted was CRTP. This is not correct. It is indeed a mixin, please read Daniel Mahler's answer lower on the page for the correct explanation.
Original: It is ok to use such a design. WTL uses it for example. It is used to implement Static Polymorphism and is called Curiously recurring template pattern
Upvotes: 10
Reputation: 43662
What you're trying to do is to inherit from two classes which may have a common base class, is that correct? In that case you shoul deal with virtual inheritance problems (i.e. you'll have to declare as virtual the inheritance of the base class for both the two classes you're interested in). That would just cause a small (likely insignificant) overhead due to some runtime support (2 vpointers more).
Your code isn't the CRTP (in CRTP the base class is the one templated receiving the derived class) and doesn't seem to address in any way the double inheritance problem you was trying to get rid of.
As far as I can see it you can either accept virtual inheritance and use the virtual keyword incurring in a minimal overhead or you can refactor your code.
I didn't completely understand what you're trying to do but if you're trying to inherit from two different classes with a common base class (virtual inheritance is all about this) and for some reason you don't want to use the virtual keyword, then you might use the CRTP in the following fashion:
#include <iostream>
using namespace std;
template<class Derived>
class Base
{
public:
void basefunc() { cout << "base here"<< endl; }
virtual void polyfunc() { cout << "base poly here"<< endl; }
};
class Derived : public Base<Derived>
{
public:
void derivedfunc() { cout << "derived here"<< endl; }
virtual void polyfunc() { cout << "derived poly here"<< endl; }
};
class OtherDerived : public Base<OtherDerived>
{
public:
void otherderivedfunc() { cout << "otherderived here"<< endl; }
virtual void polyfunc() { cout << "otherderived poly here"<< endl; }
};
class InheritingFromBoth : public Derived, public OtherDerived
{
public:
void inheritingfunc() { cout << "inheritingfromboth here" << endl; }
virtual void polyfunc() { cout << "inheritingfromboth poly here"<< endl; }
};
int main() {
Derived obj;
OtherDerived obj2;
InheritingFromBoth *obj3 = new InheritingFromBoth();
Derived *der = dynamic_cast<Derived*>(obj3);
der->polyfunc();
OtherDerived *der2 = dynamic_cast<OtherDerived*>(obj3);
der2->polyfunc();
Base<Derived>* bptr = dynamic_cast<Base<Derived>*>(obj3);
bptr->polyfunc();
Base<OtherDerived>* bptr2 = dynamic_cast<Base<OtherDerived>*>(obj3);
bptr2->polyfunc();
return 0;
}
by creating two different instances of the base class you'll avoid inheritance ambiguities.
A simpler, perhaps cleaner, better solution if you intend to inherit from a base and a base-derived class at the same time is the following:
if you pay attention in your class designing to potential name-hiding problems and just use polymorphism to "customize" the behavior of functions you actually want to be different (and that can be cast-controlled), then a clean hierarchy as Base -> Derived -> YourClass
could eventually solve your problems.
In your specific case your approach works, as noted by others as been used throughout many applications but I don't think can effectively solve your double inheritance issue. Eventually only the specific design case can lead to the lesser-evil solution.
Upvotes: 1
Reputation: 5064
Here is a good motto: Use templating for the types, but inheritance for behavior.
Stick to it. There are certainly a lot of short cuts / tricks that you might use to get the work done, but in the long run these bad design choices will be headaches. If you want to use such, make sure to research the benefits and drawbacks.
Now, going back to your question, what you asked is possible to do: see CRTP, and Static polymorphism.
Upvotes: 5
Reputation: 38800
This is fine, as Zadirion points out. The reason it works (simplified) is that templates in C++, unlike generics in C#, are compile-time. It would be remiss of me to say "it's a typedef" and I'd get a lot of flak for it, but let's keep it simple and say it was.
Consider:
class base {
protected:
base() { };
virtual ~base() { };
};
template<class T>
class super : public T {
};
and later:
super<base> s;
Absolutely fine. This is actually a rather beautiful construct. Because it is compile time, you can choose your base class, which in some design idioms could be very favourable.
Upvotes: 8
Reputation: 10495
It sounds like you are talking about the Curiously Recurring Template Pattern, which is valid C++.
Upvotes: 4