C ++虚拟模板方法

时间:2011-11-01 14:31:53

标签: c++ templates virtual

我有一个抽象类(我知道它不会以这种方式编译,但它是为了理解我想要做的事情):

class AbstractComputation {
    public:
        template <class T> virtual void setData(std::string id, T data);
        template <class T> virtual T getData(std::string id);
};

class Computation : public AbstractComputation {
    public:
        template <class T> void setData(std::string id, T data);
        template <class T> T getData(std::string id, T data);
};

因此,当我调用setData<double>("foodouble", data)时,我希望将foodouble标识的双精度(此处不是主要关注的内部机制)设置为双精度数据。

那怎么办呢?

我认为通过输入类似virtual void setData<double>(std::string id, double data)的内容可能有一个意思,但我不知道该怎么做。

6 个答案:

答案 0 :(得分:44)

问题是你不能轻易地将静态时间多态(模板)与运行时多态混合。在您的示例中禁用特定构造的语言的原因是可能有无限的不同类型可以实例化您的模板成员函数,这反过来意味着编译器必须生成代码以动态分派那些类型,是不可行的。

这里可以通过不同的方式来解决限制,基本上要么取消静态或动态多态。从等式中删除动态多态可以通过提供一个非派生类型来存储<key,value>映射,然后提供仅在基级解析的模板来完成:

class AbstractComputation {
public:
   template <typename T>
   void setData( std::string const & id, T value ) {
      m_store.setData( id, value );
   }
   template <typename T>
   T getData( std::string const & id ) const {
      return m_store.getData<T>( id );
   }
protected:
   ValueStore m_store;
};

现在派生类可以从基础访问ValueStore,并且不需要多态。 (这也可以通过直接在AbstractComputation中实现功能来完成,但分离问题可能是有意义的)

另一种选择是维护运行时多态性,但删除静态多态性。这可以通过在基类上执行类型擦除然后调度到采用类型擦除参数的适当(非模板化)函数来完成。最简单的版本就是使用boost::any

class AbstractComputation {
public:
   template <typename T>
   void setData( std::string const & id, T value ) {
      setDataImpl( id, boost::any( value ) );
   }
   template <typename T>
   T getData( std::string const & id ) const {
      boost::any res = getDataImpl( id );
      return boost::any_cast<T>( res );
   }
protected:
   virtual void setDataImpl( std::string const & id, boost::any const & value ) = 0;
   virtual boost::any getDataImpl( std::string const & id ) const = 0;
};

如何在引擎盖下实现类型擦除很有意思,但是在这里的范围之外,重要的部分是boost::any是一个具体的(非模板化的)类型,可以存储 any 在内部通过对参数使用类型擦除进行类型化,同时允许对数据进行类型安全检索。

答案 1 :(得分:14)

在某些情况下,将模板从方法级别移动到类级别可能就足够了,例如:

#include <iostream>

template<typename T>
class AbstractComputation {
public:
    virtual void setData(std::string id, T data)
    {
        std::cout << "base" << std::endl;
    }
};

template<typename T>
class Computation : public AbstractComputation<T> {
public:
    virtual void setData(std::string id, T data)
    {
        std::cout << "derived" << std::endl;
    }
};

int main()
{
    AbstractComputation<int> *x = new Computation<int>();

    x->setData("1", -1);

    delete x;
    return 0;
}

答案 2 :(得分:2)

首先,您不能拥有virtual模板功能。由于模板在编译时被解析,virtual将无效,因为编译器不知道要选择哪个模板。有关详情,请参阅here

答案 3 :(得分:1)

使用boost::any接受原点,然后在实际设置时,从中获取正确的类型。

答案 4 :(得分:1)

您可以在案件中使用boost::any

virtual void setData(std::string id, boost::any data);

它是一个可以封装几乎任何东西的包装器。

More info on a similar topic in this answer

答案 5 :(得分:1)

如果您事先知道可能的类型列表,预处理器可能有所帮助:

#define MY_CLASSES MYTYPE(int) MYTYPE(float) MYTYPE(double)

class AbstractComputation {
    public:
#     define MYTYPE(T) virtual void setData(std::string id, T data)=0;\
                       virtual void getData(std::string id, T& dst_data)=0;
      MY_CLASSES
#     undef MYTYPE
};

class Computation : public AbstractComputation {
    public:
#     define MYTYPE(T) virtual void setData(std::string id, T data){std::cout<<"writing: "<<data<<std::endl;}\
                       virtual void getData(std::string id, T& dst_data){dst_data=0;/*put your actual implementation here*/}
      MY_CLASSES
#     undef MYTYPE
};

如果您不知道可能类型的完整列表,也许您的问题无法解决。如其他人所述,类型擦除也可能有所帮助......但并非在所有情况下都有帮助。