9

One of the main benefits of virtual in C++ is being able to use the base class (pointer or reference) to call derived methods.

I'm reading up on using CRTP to implement static polymorphism, but I can't understand how to achieve what I've mentioned above using this technique, because I can't declare a function as taking type Base when this requires a template.

It seems to me that what is described in the article could be achieved by simply using function overloading, so I'm sure that there must be more to this technique.

(PS: this exact problem is alluded to in a comment to an answer to this question, but unfortunately no one had replied to it: "What vtables truly provide is using the base class (pointer or reference) to call derived methods. You should show how it is done with CRTP here.")

Here is my minimal code, which gives the error "missing template arguments before ‘&’ token void Print(Base& Object)".

#include <cstring>
#include <iostream>

template <typename Derived>
struct Base
{
    std::string ToStringInterface() { return static_cast<Derived*>(this)->ToString(); }

    std::string ToString()  {   return "This is Base.";     }
};

struct Derived : Base<Derived>
{
    std::string ToString()  {   return "This is Derived.";  }
};

void Print(Base& Object)
{
    std::cout << Object->ToStringInterface() << std::endl;
}

int main()
{
    Derived MyDerived;

    // This works, but could have been achieved with a function overload.
    std::cout << MyDerived.ToStringInterface() << std::endl;

    // This does not work.
    Print(MyDerived);
}
1
  • "because I can't declare a function as taking type Base when this requires a template." The function Print must be a function template for static polymorphism, yes -- something like template<class Derived> void Print(Base<Derived>& Object);. Static polymorphism is resolved at compile-time, so Print must know exactly which function to call at compile-time. Commented Jun 10, 2014 at 20:27

3 Answers 3

6

Thanks to the comments and answers received, I'm posting my implementation, in case it may come in useful to anyone else.

#include <cstring>
#include <iostream>

template <typename Derived>
class Base
{
public:
    std::string ToStringInterface()
    {
        return static_cast<Derived*>(this)->ToString();
    }
};

template<>
class Base<void> : public Base<Base<void> >
{
public:
    std::string ToString()
    {
        return "This is Base (default implementation).";
    }
};

class Derived : public Base<Derived>
{
public:
    std::string ToString()
    { 
        return "This is Derived.";
    }
};

template <typename T>
void Print(Base<T>& Object)
{
    std::cout << Object.ToStringInterface() << std::endl;
}

int main()
{   
    int Decision;
    std::cout << "Do you want to create an object of type Base (input 0) or Derived (input 1)? ";
    std::cin >> Decision;
    if (Decision == 0)
    {
        Base<void> MyBase;
        Print(MyBase);
    }
    else
    {
        Derived MyDerived;
        Print(MyDerived);
    }
}
Sign up to request clarification or add additional context in comments.

Comments

5

Well, you need to declare print a template function :

template<class T>
void Print(Base<T>& Object)
{
    std::cout << Object.ToStringInterface() << std::endl;
}

2 Comments

Exactly what I needed. A small follow-up question: am I right that in this case I can't have a default method for Base, and can't instantiate objects of type Base? (which can be done with dynamic polymorphism).
You could create an instance of type Base<Whatever> that isn't actually a Whatever, but calling ToStringInterface() would invoke undefined behaviour. Make the ctor protected and/or check the correct type in ToStringInterface(). Note that checking with a dynamic_cast isn't always possible, because CRTP doesn't necessarily involve any virtual functions, but those are required for such a cast.
3

Sorry, but it CRTP indeed doesn't work that way. The idea is typically to inject some code into the dependency hierarchy, in a way that is very specific to C++. In your example, you could have e.g. an interface that requires the ToStringInterface() function and use CRTP to bind it to an existing class hierarchy's ToString():

class IStringable
{
    virtual string ToStringInterface() = 0;
};
class Unchangeable
{
    virtual string ToString();
};
template<class Derived>
class UnchangeableToIStringableMixin
{
    virtual string ToStringInterface()
    {
        return static_cast<Derived*>(this)->ToString();
    }
};
class StringableUnchangeable:
    public Unchangeable, UnchangeableToIStringableMixin<StringableUnchangeable>
{
};

However, if Unchangeable can actually be changed, you wouldn't do something like that. Don't forget to consider the possibility that CRTP just isn't the right tool for what you are doing.

2 Comments

Thanks. I was doing this more as an exercise in templates and to satisfy my curiosity, than anything else. In practice I can't see why I would go with all this complexity rather than just using virtual. Eventually I found a solution I liked by specializing the Base class with void (see below).
@Ulrich IMHO you don't need virtual on the member functions, even this is the main advantage of CRTP, kind of doing virtual at compile time, you will have a faster exec as it do not need the browse the virtual table at running time.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.