实现模板化模板方法

时间:2016-10-16 20:39:55

标签: c++ templates design-patterns template-method-pattern

注意:以下问题与Template Method Design Pattern 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 个答案:

答案 0 :(得分:4)

听起来像是CRTP的一个很好的用例。将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);

用法非常简单:

[
 { "n1_0":1,
   "n1_1":15,
   "n1_2":23,
   "n1_3":0
 } //finish data.getJSONObject(0);
]

live demo

答案 1 :(得分:2)

CRTP通过将Base设为模板来解决您的问题。

如果T来自有限集,或者转换是非会话,则类型擦除可以正常工作。

如果是有限集,则键入擦除所有派生的虚拟方法。如果是common属性,则键入erase该属性和virtualize方法。或混合物。

否则,Base可以使用模板方法将操作作为函数对象(使用模板operator()),而不是使用virtual来查找它。 Derived将模板化操作作为参数传递给Base方法。这基本上是没有CRTP的CRTP。