简体   繁体   中英

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?

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

CRTP solves your problem by making Base a template.

If T comes from a finite set, or conversion is non arbutrary, type erasure can work.

If a finite set, type erase all the derived virtual methods. If common property, type erase that property and virtualize method that acts on it. Or a mixture.

Otherwise, Base can have template methods that take the operation as function object (with template operator() ) instead of using virtual to find it. Derived passes the templated operations in as arguments to Base method(s). This is basically CRTP without CRTP.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM