Реализация шаблонного метода

Примечание. Следующий вопрос касается шаблона разработки метода шаблона и шаблонов функций C++. Чтобы различать оба, я буду использовать курсив при обращении к шаблону проектирования и жирный при обращении к шаблонам C++.

Идея шаблона метода шаблона состоит в том, чтобы сделать части алгоритма взаимозаменяемыми. Обычно это достигается с помощью наследования, где подкласс обеспечивает конкретные реализации, которые подключены к алгоритму базового класса. Однако, если методы ловушек должны быть шаблонами, это не будет работать, поскольку шаблоны не могут быть виртуальными. Вот простой пример, который не компилируется:

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);
}

Есть ли способ реализовать шаблонные методы, которые используют хуки шаблонов функций?

Я не заинтересован в использовании Base класс как тип где угодно. Я всегда буду использовать конкретный конкретный тип для достижения максимальной оптимизации во время компиляции. Итак, еще одна формулировка этого вопроса: как я могу создать несколько классов Derived-1 .. Derived-n которые имеют шаблоны функций, которые имеют общий скелет кода во всех реализациях?

2 ответа

Решение

Похоже, хороший вариант использования для CRTP. определять Base как шаблон класса с типом, полученным из него в качестве параметра шаблона. внутри Baseметоды, которые вы можете привести к производному типу:

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;
    }
};

А затем определите производные типы, например:

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;
    }
};

использование довольно тривиально:

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

живое демо

CRTP решает вашу проблему, делая Base шаблоном.

Если T происходит из конечного набора, или преобразование не произвольно, стирание типа может работать.

Если конечный набор, тип стереть все производные виртуальные методы. Если это общее свойство, введите стереть это свойство и виртуализировать метод, который действует на него. Или смесь.

В противном случае у Base могут быть методы шаблона, которые принимают операцию как объект функции (с шаблоном operator()) вместо использования виртуального, чтобы найти его. Производный передает шаблонные операции в качестве аргументов базовому методу (методам). Это в основном CRTP без CRTP.

Другие вопросы по тегам