可以使用模板多态来代替OO多态吗?

Jam*_*hek 22 c++ oop templates

我试图将模板编程(以及在未来的某些方面,模板元编程)应用于现实世界的场景.我发现的一个问题是C++模板和多态并不总是按照我想要的方式一起玩.

我的问题是,我试图应用模板编程的方式是不正确的(我应该使用普通的旧OOP),或者我是否仍然坚持OOP思维模式.

在这种特殊情况下,我试图使用策略模式解决问题.我一直遇到这样的问题,我最终想要一些表现形式多样的模板似乎不支持.

OOP代码使用组成:

class Interpolator {
   public:
     Interpolator(ICacheStrategy* const c, IDataSource* const d);
     Value GetValue(const double);
}

void main(...) {
    Interpolator* i;
    if(param==1)
       i = new Interpolator(new InMemoryStrategy(...), new TextFileDataSource(...));
    else if(param==2)
       i = new Interpolator(new InMemoryStrategy(...), new OdbcDataSource(...));
    else if(param==3)
       i = new Interpolator(new NoCachingStrategy(...), new RestDataSource(...));

    while(run) {
       double input = WaitForRequest();
       SendRequest( i->GetValue(input));
    }
}
Run Code Online (Sandbox Code Playgroud)

潜在模板版本:

class Interpolator<class TCacheStrategy, class TDataSource> {
   public:
     Interpolator();
     Value GetValue(const double);               //may not be the best way but
     void ConfigCache(const& ConfigObject);      //just to illustrate Cache/DS         
     void ConfigDataSource(const& ConfigObject); //need to configured

}

//Possible way of doing main?
void main(...) {
    if(param==1)
       DoIt(Interpolator<InMemoryStrategy,TextFileDataSource>(),c,d);
    else if(param==2)
       DoIt(Interpolator<InMemoryStrategy,OdbcDataSource>(),c,d)
    else if(param==3)
       DoIt(Interpolator<NoCachingStrategy,RestDataSource>(),c,d)

}

template<class T>
void DoIt(const T&  t, ConfigObject c, ConfigObject d) {
   t.ConfigCache(c);
   t.ConfigDataSource(c);
   while(run) {
      double input = WaitForRequest();
      SendRequest( t.GetValue(input));
   }
}
Run Code Online (Sandbox Code Playgroud)

当我尝试将OOP实现转换为基于模板的实现时,可以毫不费力地翻译Interpolator代码.基本上,将"interfaces"替换为Template类型参数,并添加一种机制来传递Strategy/DataSource实例或配置参数.

但是当我进入"主要"时,我不清楚如何利用模板元编程风格的模板来编写.我经常想要使用多态,但它似乎不能很好地与模板一起使用(有时,感觉我需要Java的类型擦除泛型......呃).

当我经常发现我想要做的事情就像TemplateType<?,?> x = new TemplateType<X,Y>()x在哪里不关心X,Y是什么.

事实上,这在使用模板时经常是我的问题.

  1. 我是否需要再应用一个级别的模板?
  2. 我是否尝试使用闪亮的新型电源模板扳手将OOP钉子安装到PCI插槽中?
  3. 或者我只是在模板编程时想到这一切都错了?

[编辑]一些人已经指出这实际上不是模板元编程,所以我稍微重写了这个问题.也许这就是问题的一部分 - 我还不知道TMP究竟是什么.

Ste*_*sop 24

模板提供静态多态性:您在实现策略的编译时指定模板参数.它们不提供动态多态性,您可以在运行时为实现策略的虚拟成员函数提供对象.

您的示例模板代码将创建三个不同的类,每个类包含所有Interpolator代码,使用不同的模板参数编译,并可能从它们内联代码.这可能不是你想要的代码大小的POV,尽管它没有任何明显的错误.假设您正在优化以避免函数调用开销,那么它可能是对动态多态性的改进.更有可能是过度杀伤.如果要动态使用策略模式,则不需要模板,只需在相关时进行虚拟调用.

您不能拥有类型的变量MyTemplate<?>(除非在实例化之前出现在另一个模板中).MyTemplate<X>并且MyTemplate<Y>是完全不相关的类(即使X和Y是相关的),如果它们是从同一个模板(它们不需要它们 - 一个可能是专门化的)实例化的话,也许恰好具有类似的功能.即使它们是,如果模板参数涉及任何成员函数的签名,那么这些函数也不相同,它们只是具有相同的名称.因此,从动态多态性的POV来看,同一模板的实例与任何两个类处于相同的位置 - 只有在为它们提供具有一些虚拟成员函数的公共基类时,它们才能起作用.

因此,您可以定义一个公共基类:

class InterpolatorInterface {
public:
    virtual Value GetValue(const double) = 0;
    virtual void ConfigCache(const& ConfigObject) = 0;
    virtual void ConfigDataSource(const& ConfigObject) = 0;
    virtual ~InterpolatorInterface() {}
};
Run Code Online (Sandbox Code Playgroud)

然后:

template <typename TCacheStrategy, typename TDataSource>
class Interpolator: public InterpolatorInterface {
    ...
};
Run Code Online (Sandbox Code Playgroud)

现在你正在使用模板根据编译时已知的内容创建不同类型的Interpolator(因此从插值器到策略的调用是非虚拟的),并且你使用动态多态来对它们进行相同处理即使你直到运行时才知道你想要哪一个(因此从客户端到内插器的调用是虚拟的).你只需要记住,这两种技术几乎都是完全独立的技术,而决定使用它们的方法几乎是无关的.

顺便说一句,这不是模板元编程,它只是使用模板.

编辑.至于TMP是什么,这是规范的介绍性示例:

#include <iostream>

template<int N>
struct Factorial {
    static const int value = N*Factorial<N-1>::value;
};

template<>
struct Factorial<0> {
    static const int value = 1;
};

int main() {
    std::cout << "12! = " << Factorial<12>::value << "\n";
}
Run Code Online (Sandbox Code Playgroud)

观察那12!由编译器计算,是一个编译时常量.这很令人兴奋,因为事实证明C++模板系统是图灵完备的编程语言,C预处理器不是.根据资源限制,您可以在编译时进行任意计算,避免在编译时知道输入的情况下的运行时开销.模板可以像操作函数一样操作模板参数,模板参数可以是整数或类型.或者函数,虽然那些在编译时不能被"调用".或者其他模板,尽管那些模板不能作为结构的静态成员"返回".

  • +1表示这不是模板元编程. (3认同)

jon*_*son 7

我发现模板和多态性很好地工作.在您的示例中,如果客户端代码不关心使用哪些模板参数Interpolator,则引入模板子类的抽象基类.例如:

class Interpolator
{
public:
    virtual Value GetValue (const double) = 0;
};

template<class TCacheStrategy, class TDataSource>
class InterpolatorImpl : public Interpolator
{
public:
     InterpolatorImpl ();
     Value GetValue(const double);
};

void main()
{
    int param = 1;

    Interpolator* interpolator = 0;

    if (param==1)
        interpolator = new InterpolatorImpl<InMemoryStrategy,TextFileDataSource> ();
    else if (param==2)
        interpolator = new InterpolatorImpl<InMemoryStrategy,OdbcDataSource> ();
    else if (param==3)
        interpolator = new InterpolatorImpl<NoCachingStrategy,RestDataSource> ();

    while (true)
    {
        double input = WaitForRequest();
        SendRequest( interpolator->GetValue (input));
    }
}
Run Code Online (Sandbox Code Playgroud)

我用这个成语很多.它非常好地隐藏了客户端代码中的模板内容.

注意,我不确定这些模板的使用真的是类"元编程".我通常保留使用更复杂的编译时模板技巧的宏伟术语,尤其是条件,递归定义等的使用,以便在编译时有效地计算内容.


jal*_*alf 6

模板有时被称为静态(或编译时)多态,所以是的,它们有时可以用来代替OOP(动态)多态.当然,它需要在编译时确定类型,而不是运行时,因此它不能完全取代动态多态.

当我经常发现我想做的事情就像TemplateType x = new TemplateType(),其中x不关心X,Y是什么.

是的,那是不可能的.您必须执行类似于DoIt()函数的操作.通常情况下,我认为最终会得到一个更清晰的解决方案(最终你会得到更小的功能,每个功能只做一件事 - 通常是一件好事).但是,如果类型仅在运行时确定(与i主函数的OOP版本一样),则模板将不起作用.

但在这种情况下,我认为您的模板版本可以很好地解决问题,并且本身就是一个很好的解决方案.(尽管有人提到过,但它确实意味着所有三个模板的代码都被实例化,这在某些情况下可能会成为一个问题)