Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 327
Nico Schertler Avatar asked Dec 18 '22 12:12

Nico Schertler


1 Answers

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

like image 182
krzaq Avatar answered Dec 21 '22 03:12

krzaq