Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Curiously Recurring Template Pattern

15 views
Skip to first unread message

chsa...@gmail.com

unread,
Jul 10, 2007, 7:22:57 PM7/10/07
to
The "Curiously Recurring Template Pattern" (CRTP) recently caught my
attention as a nice trick which allows static polymorphism. In other
words, it lets you build extensible classes without the overhead of
virtual function calls. (But of course, with the limitation of
compile-time resolving.)

Basically, you create a templated Base Class which defines an
interface function that uses a static_cast to call a member function
in Derived. Then the Derived class inherits from the templated Base
class with Derived as a template argument.

template <class Derived>
struct Base
{
void work()
{
static_cast<Derived*> (this)->work();
}
};

struct Derived : public Base<Derived>
{
void work()
{
cout << "Derived class function." << endl;
}
};

template <class T>
void func(Base<T>& Object)
{
Object.work();
}

int main()
{
Derived D;
func(D);
}

This is a nice way to make extensible classes without using runtime
polymorphism. But the more I think about it, it seems kind of
pointless. If you want to build extensible classes with static
polymorphism, you can just use templates to simply do something like
this:

template <class T>
void func(T& Object)
{
Object.work();
}

This way any class that defines a work() member function can be passed
to func(), and the correct member function will be called. This is a
lot simpler than CRTP. So what's the real advantage of CRTP then?
The only advantage I see is that CRTP works with the inheritance
hierarchy, just like normal polymorphism, rather than letting you use
any class which defines a certain member function name.

Am I correct here, or am I overlooking the value of CRTP somehow?

Zeppe

unread,
Jul 11, 2007, 4:38:13 AM7/11/07
to

It's very different. Think about that, instead:

int main()
{
Base* d = new Derived();
func(*d);
}

ups! Your template approach will fail! But CRTP don't. The main
advantage of CRTP is that you can lose the reference to the real
instantiated class without losing the polymorphism. This is very useful,
for example, when you want to build a heterogeneus container like:

std::vector<Base*> v;

(Notice that you may still need a virtual destructor if the container is
the owner of the objects).

Regards,

Zeppe

Dizzy

unread,
Jul 11, 2007, 6:13:22 AM7/11/07
to
Zeppe wrote:

> It's very different. Think about that, instead:
>
> int main()
> {
> Base* d = new Derived();
> func(*d);
> }

That cannot work with CRTP. You probably mean Base<Derived>* d = new
Derived(); otherwise it doesn't compile ("Base" is a family of structs,
there is no type Base from which you derived, but instead you derive from
Base<Derived>).


> ups! Your template approach will fail! But CRTP don't. The main
> advantage of CRTP is that you can lose the reference to the real
> instantiated class without losing the polymorphism.

No you cannot lose the type, that's the point (otherwise compile time
resolving of the function to call wouldnt work). The base class when
instantiated has all the knowledge about the Derived. So you cannot lose
it, you have it there all along so you need template functions as the OP
said.

> This is very useful,
> for example, when you want to build a heterogeneus container like:
>
> std::vector<Base*> v;

Again impossible with CRTP.

> (Notice that you may still need a virtual destructor if the container is
> the owner of the objects).

Maybe you mean something different than the OP and as such I am missing your
point?

--
Dizzy

Michael DOUBEZ

unread,
Jul 11, 2007, 8:02:25 AM7/11/07
to
chsa...@gmail.com a écrit :

> The "Curiously Recurring Template Pattern" (CRTP) recently caught my
> attention as a nice trick which allows static polymorphism.
> [snip]

> This is a nice way to make extensible classes without using runtime
> polymorphism. But the more I think about it, it seems kind of
> pointless. If you want to build extensible classes with static
> polymorphism, you can just use templates to simply do something like
> this:
>
> template <class T>
> void func(T& Object)
> {
> Object.work();
> }
>
> This way any class that defines a work() member function can be passed
> to func(), and the correct member function will be called. This is a
> lot simpler than CRTP. So what's the real advantage of CRTP then?
> The only advantage I see is that CRTP works with the inheritance
> hierarchy, just like normal polymorphism, rather than letting you use
> any class which defines a certain member function name.
>
> Am I correct here, or am I overlooking the value of CRTP somehow?
>

CRTP splits generic and concrete functionality by delegating to its
derived class.

Concerning function call, there is no difference with a simple template
where the delegation is made by composition (as in your example).

IMO, what CRTP allows is in particular:
- class width operations (typically counting the instances of a class)
- static polymorphism which allows the inheritance or cooperation of
traits and useful specialization under metaprogramming (factorizing
operations by example).
- marginally, the call to protected members without virtual call
overhead(http://accu.org/index.php/journals/296).

Michael

Zeppe

unread,
Jul 11, 2007, 9:22:35 AM7/11/07
to
Dizzy wrote:

> No you cannot lose the type, that's the point (otherwise compile time
> resolving of the function to call wouldnt work). The base class when
> instantiated has all the knowledge about the Derived. So you cannot lose
> it, you have it there all along so you need template functions as the OP
> said.

[snip]


> Maybe you mean something different than the OP and as such I am missing your
> point?
>

Not at all. My bad, I didn't pay attention while answering. Obviously
you can't lose the type, as you said. I guess then that there are no
differences with a simple template at a functionality level, but I think
it makes sense to integrate the static polymorphism at a class level
(i.e., not via a template function call). For example, this principle
can enable for the implementation of functionalities through composition
(as Michael said, counting elements; another example can be the
singleton class functionality)...

Regards,

Zeppe

0 new messages