c++templatesdesign-patternstemplate-method-pattern

Implementing templated template method


Note: The following question is about the Template Method Design Pattern and C++ function templates. To distinguish both, I will use italics when referring to the design pattern and bold when referring to C++ templates.

The idea of the template method pattern is to make parts of an algorithm exchangeable. This is usually achieved via inheritance, where the subclass provides concrete implementations that are plugged into an algorithm of the base class. However, if the hook methods need to be templates, this will not work as templates cannot be virtual. Here is a simple example that does not compile:

class Base
{
public:

    // This is the template method
    template <typename T>
    void doSomething(T input)
    {
        //...
        auto converted = ConvertInput(input);
        //...
        std::cout << converted;
    }

protected:
    //compile error "member function templates cannot be virtual"
    template <typename T>
    virtual T ConvertInput(T input) = 0;
};

class Derived : public Base
{
protected:
    template <typename T>
    T ConvertInput(T input)
    {
        return 2 * input;
    }
};

int main()
{
    Derived d;
    d.doSomething(3);
}

Is there a way to implement template methods that use function template hooks?

I am not interested in using the Base class as a type anywhere. I will always use the concrete specific type to achieve a maximum of compile-time optimization. So another formulation of this question is: How can I create several classes Derived-1 .. Derived-n that have function templates that share a common code skeleton across the implementations?


Solution

  • Sounds like a fine use-case for CRTP. Define Base as a class template with the type derived from it as the template parameter. Inside Base's methods you can cast down to the derived type:

    template<typename Derived>
    struct Base
    {
        // This is the template method
        template <typename T>
        void doSomething(T input)
        {
            //...
            auto converted = static_cast<Derived*>(this)->ConvertInput(input);
            //...
            std::cout << converted << std::endl;
        }
    };
    

    And then define the derived types, for example:

    struct Square : Base<Square>
    {
        template<typename T>
        auto ConvertInput(T t)
        {
            return t*t;
        }
    };
    
    struct Sum : Base<Sum>
    {
        template<typename T>
        auto ConvertInput(T t)
        {
            return t+t;
        }
    };
    

    the usage is pretty trivial:

    Square sq;
    Sum sum;
    sq.doSomething(3);
    sum.doSomething(3);
    

    live demo