I understand what CRTP is and how/why it works the way it does, but I'm struggling to under why I'd ever want to use it over alternatives.
The most common reason I hear is that CRTP is used to implement static polymorphism in order to avoid the overhead of using vtables. But isn't it possible to implement static polymorphic behaviour even without virtual methods?
The main reason I use virtual is when I want something like:
Code:
std::vector<Base*> v;
DerivedA a;
DerivedB b;
v.push_back(&a);
v.push_back(&b);
for (auto obj_ptr : v) { obj_ptr->hello(); }
I don't believe this can be achieved with CRTP because the type of an object needs to be known at compile time for CRTP to work. Also since the following two classes have no common ancestor, I can't use subtype polymorphism (I believe that's what it's called).
Code:
struct DerivedA : public Base<DerivedA> {};
struct DerivedA : public Base<DerivedB> {};
This is an example which I believe is similar to the ones commonly used in articles talking about CRTP.
Code:
#include <iostream>
template <typename T>
struct Base
{
void interface(int i)
{
static_cast<T*>(this)->implementation(i);
}
};
struct Derived_A : public Base<Derived_A>
{
void implementation(int i)
{
std::cout << "Derived_A: " << i << "\n";
}
};
struct Derived_B : public Base<Derived_B>
{
void implementation(int i)
{
std::cout << "Derived_B: " << i << "\n";
}
};
template <typename T>
void many_print(Base<T> &obj, int limit)
{
for (int i = 0; i < limit; i++)
{
obj.interface(i);
}
}
struct Bad {};
int main()
{
Derived_A a;
a.interface(10);
Derived_B b;
b.interface(20);
many_print(a, 10);
many_print(b, 20);
// Bad bad;
// many_print(bad, 30);
return 0;
}
But I can achieve this without CRTP and without vtables:
Code:
#include <iostream>
struct Base
{
void interface(int i);
};
struct Derived_A : public Base
{
void interface(int i)
{
std::cout << "Derived_A: " << i << "\n";
}
};
struct Derived_B : public Base
{
void interface(int i)
{
std::cout << "Derived_B: " << i << "\n";
}
};
template <typename T>
void many_print(T &obj, int limit)
{
for (int i = 0; i < limit; i++)
{
obj.interface(i);
}
}
struct Bad {};
int main()
{
Derived_A a;
a.interface(10);
Derived_B b;
b.interface(20);
many_print(a, 10);
many_print(b, 20);
// Bad bad;
// many_print(bad, 30);
return 0;
}
So what exactly can I do with CRTP that I can't do with classes that don't use vtables? I'd appreciate a straight to the point example please