Templates vs. Virtual functions

You have probably heard about polymorphism before. Well, there are at least two different kinds of polymorphism in C++. Today, we’re going to talk about run time polymorphism via inheritance and compile time polymorphism via templates. Most people are familiar with polymorphism via inheritance. Typically we encounter it early in our programming career, often via some strange example about animals making various noises. Template polymorphism is not so well loved, but in this blog, we are going to give it a chance!

What is polymorphism anyway?

Polymorphism is type abstraction. We are doing polymorphism, when, we ignore some of the specifics of a given type, and treat it as different type, for example through an interface. The best way to understand it is via examples.

Polymorphism via inheritance

Let’s try our hand at a little subclass polymorphism. We are going to create a very simple base class with a virtual method. We will also create a subclass that inherits form this base class, and overrides our virtual base class method.

class SimpleBaseClass
{
public:
    virtual void DoSomethingFun() 
    {
        // put some fun stuff in here
    }
}

class SimpleSubClass : public SimpleBaseClass
{
public:
    void DoSomethingFun()
    {
        // put some different fun stuff here
    }
}

Now we can provide many different implementation of the DoSomethingFun method in different subclasses, and we can use them like so:

int main()
{
    SimpleBaseClass* polyObject = new SimpleSubClass();
    polyObject->DoSomethingFun();
    return 0;
}

When this all compiles, the compiler will create a special lookup table called a vtable. Then, because this is a virtual method, at run time, when the DoSomethingFun method is called on the polyObject pointer, the vtable will be used to find the implementation in the SimpleSubClass. That was polymorphism!

Polymorphism via templates

We have covered the bread and butter of polymorphism. However, we can achieve the same effect with templates! To do this, we are going to define a templated class, and two different implementation classes:

class FunImplementationClass
{
public:
    FunImplementation()
    {
        // put some fun stuff here
    }
}

class AnotherFunImplementationClass
{
public:
    FunImplementation()
    {
        // put some different fun stuff here
    }
}

template <typename T>
public TemplateBaseClass<T>
{
private:
    T implementation;
public:
    TemplateBaseClass(T implementation) : implementation(implementation)
    {
    }

    void DoSomethingFun()
    {
        implementation.FunImplementation();
    }
}

Now we have created a sort of base class like before. But, instead of defining different implementations in subclasses that inherit from this base class, we define them in classes that we pass in via template type parameters.

And we can use this in a very similar (and slightly more verbose) way to the traditional object oriented approach:

int main()
{
    TemplateBaseClass<FunImplementationClass> polyObject = TemplateBaseClass<FunImplementationClass>(FunImplementation());
    polyObject.DoSomethingFun()
    return 0;
}

This time, the compiler will use our templating to resolve which implementation to use, and it will do this at compile time, not run time.

Why would we even want to use templates like this?

There are some disadvantages to the template approach. The most obvious of which is that template programming is not as well known. If you use it, the other people who read, maintain and extend your code, are less likely to be familiar with this technique. So it will be a lot harder for them!

The other, is that templated code is a lot harder to debug. In particular, if you have ever had a compiler error from templated code, you will recognise the error message gore it produces.

I have also heard that templated code is liable to create very large binaries, as so much extra code is generated by the compiler. However I have never seen this being a problem in real life.

With all that said, why might you sill choose to use the templated version? The main reason is performance. There is a performance penalty associated with run time polymorphism. When the correct method is resolved at run time, it requires a look up in the vtable which adds a small but non-zero performance penalty.

In addition to this, when we use compile time polymorphism, the compiler knows exactly what code path we are going to follow. When we use run time polymorphism, that will only be resolved at run time. This means that the compiler has much greater flexibility to optimise our templated code. This can give us a significant performance boost!