Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use CRTP with multiple derived classes, and use them polymorphically?

I have such hierarchy of classes:

template <class Type>
class CrtpBase
{
protected:
    Type& real_this()
    {
        return static_cast<Type&>(*this);
    }
};

template <class ChildType>
class Base : CrtpBase<ChildType>
{
public:
    void foo()
    {
        this->real_this().boo();
    }
};

class Derived1 : public Base<Derived1>
{
public:
    void boo { ... }
};

class Derived2 : public Base<Derived2>
{
public:
    void boo { ... }
};

The thing is, I want to use my classes in this way:

std::vector<Base*> base_vec;
base_vec.push_bach(new Derived1());
base_vec.push_bach(new Derived2());
.........
base_vec[0]->foo();

But this isn't possible, because base class for all derived classes is different (actually Base isn't a type at all, it's template). So, is there a way to use crtp with multiple derived classes, alongside with polymorphism?

like image 606
user1289 Avatar asked Apr 15 '16 08:04

user1289


1 Answers

Indeed there is, you need to add the appropriate non-template base class too:

class AbstractBase
{
public:
  virtual ~AbstractBase() {}

  virtual void foo() = 0;
};


template <class ChildType>
class Base : CrtpBase<ChildType>, public AbstactBase
{
  void foo() override { this->real_this().boo(); }
};

Then, declare your vector as std::vector<AbstractBase*>.

This does indeed introduce the overhead of dynamic dispatch (which you were probably trying to avoid by using CRTP), but dynamic dispatch is the only way to get runtime polymorphism in C++.

It can still be beneficial, though. For example, if the implementation of foo is shared by all the derived classes, but calls into many different boo-style functions (with each derived class having a different implementation of those), you will only pay the dynamic dispatch cost once when invoking foo, and then all the calls made within foo are dispatched statically, CRTP-style.

On the other hand, if it's just one call to a boo-like function within foo, you may as well make boo virtual, put non-virtual foo into the base, thus getting rid of CRTP. The cost will be the same then: a non-virtual dispatch (foo) and a virtual one (boo).


Side note, you should strongly consider storing smart pointers in the std::vector; owning raw pointers are bad practice.

like image 102
Angew is no longer proud of SO Avatar answered Oct 27 '22 06:10

Angew is no longer proud of SO