473,406 Members | 2,369 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,406 software developers and data experts.

Curiously Recurring Template Pattern

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?

Jul 10 '07 #1
4 3068
ch******@gmail.com wrote:
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?
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
Jul 11 '07 #2
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

Jul 11 '07 #3
ch******@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
Jul 11 '07 #4
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
Jul 11 '07 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

5
by: papi1976 | last post by:
Hello to everyone I heard about a strange thing about wich i have'nt find anything on the net or books, someone called this "Curiously recursive pattern" or a strange trick to play with...
14
by: John Harrison | last post by:
The following code does not compile template <class Derived> struct X { typedef typename Derived::type type; }; struct Y : public X<Y> {
15
by: iuweriur | last post by:
A few questions on the curiously recurring template pattern: This page: http://c2.com/cgi/wiki?CuriouslyRecurringTemplate this part: template<typename T> struct ArithmeticType { T operator...
4
by: Martin MacRobert | last post by:
Hi Gang, The following code does not compile, but I can't figure out why. The compiler complains that the CuriouslyDerivedType (CRDerived) does not publish the "value_type", yet in fact the class...
4
by: AndrewD | last post by:
Hey C++ folks, I created this today, just for fun. You can make object allocation for any class around 6 times faster, simply by doing the following. class MyClass : public...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.