2016-10-16 12 views
2

Примечание: Следующий вопрос касается Template Method Design Pattern и C++ функций шаблонов. Чтобы отличить оба, я буду использовать курсивом при обращении к шаблону проектирования и bold при обращении к шаблонам 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, которые имеют шаблоны функций, которые совместно используют общий скелет кода через реализации?

+3

[CRTP] (https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern). Сделайте 'Base' взять' Derived' в качестве параметра шаблона. Вызов 'ConvertInput' через' static_cast (this) -> ConvertInput' –

+0

Есть ли ограничение на набор типов, используемых для 'T'? – Yakk

+0

@Yakk Да, но только неявные. Например. одному экземпляру этого нужно брать итераторы, что может быть чем угодно. –

ответ

3

Звучит как прекрасный прецедент для CRTP. Определите Base как шаблон класса с типом, полученным из него в качестве параметра шаблона. Внутри методов Base «s вы можете повергнуть к производному типу:

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

live demo

+0

Спасибо. Я уже думал в этом направлении, и это, безусловно, возможное решение. Меня также интересовали бы решения, которые не требуют создания шаблона класса «Base». –

+1

+1 для CRTP: не читал прошлое. Если остальное сообщение смущает вас, просто прочитайте о CRTP, ваша проблема идеально подходит для него. – Yakk

2

CRTP решает вашу проблему маки ng Создайте шаблон.

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

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

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