What's polymorphic in CRTP

J

Jarek Blakarz

Hi

I understand what is CRTP. What I don't understand is why CRTP is called a
static polymorphism or rather polymorphism in particular.

I think that it is "static" since the function call is resolved at compile
time, right ?

Assuming that I'm right about "static", plese explain me based on the following
example why it is "polymorphic".

thanks for clarification.



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

struct Derived1 : Base<Derived1> {
void implementation() { }
};

struct Derived2 : Base<Derived2> {
void implementation() { }
};

Derived1 *d1 = new Derived1;
Derived2 *d2 = new Derived2;

d1->interface();
d2->interface();
 
S

Stuart

Hi

I understand what is CRTP. What I don't understand is why CRTP is called a
static polymorphism or rather polymorphism in particular.

Never heard of this though I have used CRTP lots of times. Most
textbooks make a difference between static and dynamic polymorphism with
regard to whether the polymorphic behaviour is resolved at compile time
or run-time. The classic example for static polymorphism is function
overloading. Although, the term may also be applied to CRTP, I guess.
I think that it is "static" since the function call is resolved at compile
time, right ?

I'd say right.
Assuming that I'm right about "static", plese explain me based on the following
example why it is "polymorphic".

thanks for clarification.



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

At this place the writer of this code cannot tell which implementation
of the invoked member function "implementation" will be called, so one
could say that the invokation of the "implementation" method on the
"this" pointer is polymorphic behaviour.

I don't like the term "static polymorphism" very much. In my opinion
"polymorphism" and "static" contradict each other. But apparently most
scientist from "applied computer science" (another contradiction?) think
otherwise.
};

struct Derived1 : Base<Derived1> {
void implementation() { }
};

struct Derived2 : Base<Derived2> {
void implementation() { }
};

Derived1 *d1 = new Derived1;
Derived2 *d2 = new Derived2;

d1->interface();
d2->interface();

Regards,
Stuart
 
Ö

Öö Tiib

Hi

I understand what is CRTP. What I don't understand is why CRTP is called a
static polymorphism or rather polymorphism in particular.

It is not called static polymorphism. It is common technique in C++ to achieve
static polymorphism.
I think that it is "static" since the function call is resolved at compile
time, right ?

Yes. You are correct. Exactly like there are arrays with static size
(std::array<T,N>) and dynamic size (std::vector<T>). Static size means lost
flexibility (that is maybe not needed) and won efficiency (that is always
great to have).
Assuming that I'm right about "static", plese explain me based on the
following example why it is "polymorphic".
template <class Derived> struct Base {
void interface() { static_cast<Derived*>(this)->implementation(); }

This may or not be bare-bone interface. This is basic part of implementation
that calls possibly "statically overriden" member function from derived class.
You can even provide default implementation here:

void implementation() { }
};

struct Derived1 : Base<Derived1> {
void implementation() { }
};

struct Derived2 : Base<Derived2> {
void implementation() { }
};

Lets take you have default and also such Derived3 here:

struct Derived3 : Base<Derived3> {
};

Derived1 *d1 = new Derived1;
Derived2 *d2 = new Derived2;
d1->interface();
d2->interface();

Your example leaks memory and feels like Java with its tons of news at
each corner. C++11 just forgot to add 'std::make_unique<T>()', if that
was in language then need to write 'new' or 'delete' in C++ code was
close to none.

Derived1 d1;
Derived2 d2;
Derived3 d3;
d1.interface();
d2.interface();
d3.interface();

It is polymorphism. All 3 calls may do different things. All 3 calls have
part (possibly everything) of what they do inherited from Base, and part
(possibly nothing) overriden. All 3 calls have exactly same common user
interface.

It is subtly less flexible and more efficient than virtual call ... so
better *you* explain ... what is your problem of calling it polymorphism?
 
J

Jarek Blakarz

It is not called static polymorphism. It is common technique in C++ to achieve

static polymorphism.







Yes. You are correct. Exactly like there are arrays with static size

(std::array<T,N>) and dynamic size (std::vector<T>). Static size means lost

flexibility (that is maybe not needed) and won efficiency (that is always

great to have).








This may or not be bare-bone interface. This is basic part of implementation

that calls possibly "statically overriden" member function from derived class.

You can even provide default implementation here:



void implementation() { }









Lets take you have default and also such Derived3 here:



struct Derived3 : Base<Derived3> {

};









Your example leaks memory and feels like Java with its tons of news at

each corner. C++11 just forgot to add 'std::make_unique<T>()', if that

was in language then need to write 'new' or 'delete' in C++ code was

close to none.



Derived1 d1;

Derived2 d2;

Derived3 d3;

d1.interface();

d2.interface();

d3.interface();



It is polymorphism. All 3 calls may do different things. All 3 calls have

part (possibly everything) of what they do inherited from Base, and part

(possibly nothing) overriden. All 3 calls have exactly same common user

interface.



It is subtly less flexible and more efficient than virtual call ... so

better *you* explain ... what is your problem of calling it polymorphism?

Thanks for explanation to everyone.
My goal was just to understand why it is called polymorphism.
Earlier I only suspected why. Now I think I got it.
 
C

Cholo Lennon

Thanks for explanation to everyone.
My goal was just to understand why it is called polymorphism.
Earlier I only suspected why. Now I think I got it.

If you are not afraid of Microsoft/Windows/COM you can take a look to
Microsoft ATL library (or its cousin WTL, a tiny header-only library
http://wtl.sourceforge.net/) in order to view an extensive usage of CRTP.

Regards
 
J

Jarek Blakarz

Thanks for the reply to all of you.
I spent some time looking at Windows examples but it seems to complex to me at
the beginning.
Could you please give me a simple example of CRTP as a replacement for dynamic
polymorphism. In case of dynamic polymorphism it seems quite obvious to me why
it is powerful. In case of CRTP I still don't feel how it can replace dynamic
polymorphism.

In case of dynamic polymorphism I can refer to the derived classes using
exactly the same interface e.g:

void fun ( Base &base )
{
base.makeSound(); // exactly the same interface for distinct derived classes
}

Unfortunately I cannot apply the same logic to CRTP static polymorphism.
In my initial example above the interface is not "exactly" the same. It is
only "similar". It differs with object name. In case of dynamic polymorphism
whenever I inherit from the base class the interface remains the same. In case
of CRTP static polymorphism whenever I inherit from the base class the
interface changes.

Thanks for the explanation.
 
Ö

Öö Tiib

Thanks for the reply to all of you.
I spent some time looking at Windows examples but it seems to complex to me at
the beginning.

WTL (static polymorphism) was made to be alternative GUI toolkit to MFC (deep
dynamic polymorphism). It sort of works so it can replace over-used dynamic
polymorphism.
Could you please give me a simple example of CRTP as a replacement for dynamic
polymorphism. In case of dynamic polymorphism it seems quite obvious to me why
it is powerful. In case of CRTP I still don't feel how it can replace dynamic
polymorphism.

Yes, whole GUI toolkit is indeed not a simple example but that is it. You want low
level techniques how to convert one to other but there are no such things. There
are real, high-level goals (like GUI toolkit) that can be achieved using one way or
other. CRTP is more efficient than virtual inheritance but it has its limitations too.
In case of dynamic polymorphism I can refer to the derived classes using
exactly the same interface e.g:

void fun ( Base &base )
{
base.makeSound(); // exactly the same interface for distinct derived classes
}

What is so hard about it?

template<class T>
void fun( T& t )
{
t.makeSound(); // works for all classes with such member
}
Unfortunately I cannot apply the same logic to CRTP static polymorphism.
In my initial example above the interface is not "exactly" the same. It is
only "similar". It differs with object name. In case of dynamic polymorphism
whenever I inherit from the base class the interface remains the same. In case
of CRTP static polymorphism whenever I inherit from the base class the
interface changes.

Template is more flexible since it does not require you to derive from same base.
Just be "similar" (your word) and you *have* same interface for a template.
If you require exact class in interface then that is what is needed.

Can you explain why you require exact class? There are only limited cases,
like when you want to use exactly same pointer to point to one or other.
That is where you need dynamic polymorphism. Achieve your goal (GUI toolkit)
without having such undetermined pointers in code and CRTP works fine.
 
C

Cholo Lennon

Thanks for the reply to all of you.
I spent some time looking at Windows examples but it seems to complex to me at
the beginning.
Could you please give me a simple example of CRTP as a replacement for dynamic
polymorphism. In case of dynamic polymorphism it seems quite obvious to me why
it is powerful. In case of CRTP I still don't feel how it can replace dynamic
polymorphism.

In case of dynamic polymorphism I can refer to the derived classes using
exactly the same interface e.g:

void fun ( Base &base )
{
base.makeSound(); // exactly the same interface for distinct derived classes
}

Unfortunately I cannot apply the same logic to CRTP static polymorphism.
In my initial example above the interface is not "exactly" the same. It is
only "similar". It differs with object name. In case of dynamic polymorphism
whenever I inherit from the base class the interface remains the same. In case
of CRTP static polymorphism whenever I inherit from the base class the
interface changes.

Thanks for the explanation.

How about this?


#include <iostream>

template<class T>
class Base {

public:
void callDynamic()
{
onDynamic();
}

void callStatic()
{
T* pThis = static_cast<T*>(this);
pThis->onStatic();
}


virtual void onDynamic()
{
std::cout << "Base::eek:nDynamic" << std::endl;
}

void onStatic()
{
std::cout << "Base::eek:nStatic" << std::endl;
}

};


class Derived: public Base<Derived> {

public:

virtual void onDynamic()
{
std::cout << "Derived::eek:nDynamic" << std::endl;
}

// "pseudo virtual" function :)
void onStatic()
{
std::cout << "Derived::eek:nStatic" << std::endl;
}

};


int main(int, char**)
{
Derived derived;
derived.callDynamic();
derived.callStatic();
}


Also, try commenting out functions in Derived class to see what happens

Regards
 
Ö

Öö Tiib

How about this?

It is example that works but does not achieve much else but inefficiency.
;-)
#include <iostream>

template<class T>
class Base {

Note that T is meant to be derived class.
public:
void callDynamic()
{
onDynamic();
}

void callStatic()
{
T* pThis = static_cast<T*>(this);
pThis->onStatic();
}

virtual void onDynamic()
{
std::cout << "Base::eek:nDynamic" << std::endl;
}

void onStatic()
{
std::cout << "Base::eek:nStatic" << std::endl;
}

};


class Derived: public Base<Derived> {

public:

virtual void onDynamic()
{
std::cout << "Derived::eek:nDynamic" << std::endl;
}

// "pseudo virtual" function :)
void onStatic()
{
std::cout << "Derived::eek:nStatic" << std::endl;
}
};


int main(int, char**)
{
Derived derived;
derived.callDynamic();
derived.callStatic();
}


Also, try commenting out functions in Derived class to see what happens

Just try making it with Derived1, Derived2 and Derived3 and you see where you
went wrong. Do not mix CRTP with dynamic polymorphism. Use typical
pure abstract interface base and multiple inheritance side by side with
CRTP if you need to mix them.
 
C

Cholo Lennon

It is example that works but does not achieve much else but inefficiency.
;-)

Why the example is inefficient? The call to Derived::eek:nStatic is
resolved at compile time.

Also, in order to simplify the code I put the dynamic/static variants in
the same class.

The example shows static polymorphism as is used in ATL (WTL). It was
not intended to be used/interpreted in another way.
 
Ö

Öö Tiib

Why the example is inefficient? The call to Derived::eek:nStatic is
resolved at compile time.

Because there are virtual functions (more expensive) that can not be used for
dynamic polymorphism (purpose is lost).
I explained it further. In my post that you quoted but apparently did not read.
 
C

Cholo Lennon

Because there are virtual functions (more expensive) that can not be used for
dynamic polymorphism (purpose is lost).
I explained it further. In my post that you quoted but apparently did not read.

and... have you read my explanation? This was an *example*, not a
production code. The idea was to show the dynamic and static variant in
a simple way.

Regards
 
Ö

Öö Tiib

and... have you read my explanation? This was an *example*, not a
production code. The idea was to show the dynamic and static variant in
a simple way.

I do not, yes, understand something here. Can you elaborate? Example must
be ideally simplest that works. Simpler than that is a lie, not example.
Someone may see it and think that it is showing how to mix dynamic and static
polymorphism, but it can't. What production code? I was saying that it should
be example on "never do like that" page with large red cross on it.
 
C

Cholo Lennon

I do not, yes, understand something here. Can you elaborate? Example must
be ideally simplest that works. Simpler than that is a lie, not example.
Someone may see it and think that it is showing how to mix dynamic and static
polymorphism, but it can't. What production code? I was saying that it should
be example on "never do like that" page with large red cross on it.

OMG, the OP asked for an example of static polymorphism with CRTP and
you gave him an example of static polymorphism with templates but
without CRTP! Nice way to teach the concept!

Just in case you insist with my example, I re-wrote it for you:


#include <iostream>

namespace dynamic_polymorphism
{
class Base {
public:
void callFoo()
{
onFoo();
}

virtual void onFoo()
{
std::cout << "Base::eek:nFoo" << std::endl;
}

};

class Derived: public Base {
public:
virtual void onFoo()
{
std::cout << "Derived::eek:nFoo" << std::endl;
}

};
}


namespace static_polymorphism
{
template<class T>
class Base {
public:
void callFoo()
{
T* pThis = static_cast<T*>(this);
pThis->onFoo();
}

void onFoo()
{
std::cout << "Base::eek:nFoo" << std::endl;
}

};

class Derived: public Base<Derived> {
public:
// "pseudo virtual" function
void onFoo()
{
std::cout << "Derived::eek:nFoo" << std::endl;
}

};
}


int main(int, char**)
{
dynamic_polymorphism::Derived der1;
der1.callFoo();

static_polymorphism::Derived der2;
der2.callFoo();
}


Are you happy now?

Regards
 
Ö

Öö Tiib

OMG, the OP asked for an example of static polymorphism with CRTP and
you gave him an example of static polymorphism with templates but
without CRTP! Nice way to teach the concept!

The concept we discussed before. CRTP is just one pattern for to use static
polymorphism, but that I said to him in my first reply already.

Now he was asking what to do with the resulting with CRTP functions that
have same signature (he said are "similar") but do not belong to same virtual
hierarchy (he said are not "same"). So I said that he should continue using
static polymorphism.
Just in case you insist with my example, I re-wrote it for you:
Thanks.

Are you happy now?

Sometimes people see me more grumpy than I really am. I try to point out the
flaws to be helpful. Everything contains mistakes, mine code too, yours was
not making me unhappy or anything.

Mixing static and dynamic polymorphism may be needed in same application.
For example if you need a container that contains polymorphic pointers then
there you have to use dynamic polymorphism, static does not work.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top