【问题标题】:C++ Virtual template methodC++ 虚拟模板方法
【发布时间】:2011-12-19 13:28:45
【问题描述】:

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

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&lt;double&gt;("foodouble", data) 时,我希望将foodouble 标识的双精度(这里不是主要关注的内部机制)设置为双精度数据。

那该怎么做呢?

我认为输入virtual void setData&lt;double&gt;(std::string id, double data) 之类的内容可能是一种意思,但我不知道该怎么做。

【问题讨论】:

  • 这个问题是关于如何为双打创建特殊情况的。或者如何通过使用一个模板函数来通用地设置任何数据类型。
  • 呃……如果一个类型有“set data”和“get data”两个方法,那么命名该类型时首先想到的是“抽象计算”吗?

标签: c++ templates virtual


【解决方案1】:

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

这里可以做不同的事情来绕过这个限制,基本上要么去掉静态多态性,要么去掉动态多态性。从方程中删除动态多态性可以通过提供一个不是派生自的类型来存储&lt;key,value&gt; 映射,然后提供仅在基本级别解决该问题的模板:

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 中实现功能来完成,但分离关注点可能是有意义的)

另一种选择是保持运行时多态性,但删除静态多态性。这可以通过在基类上执行类型擦除,然后分派到采用 type-erased 参数的适当(非模板化)函数来完成。最简单的版本就是使用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 是一个可以存储任何的具体(非模板化)类型通过在参数上使用类型擦除在内部键入,同时允许对数据进行类型安全的检索。

【讨论】:

  • 我喜欢使用 boost::any 的方法,但不喜欢添加对 Boost 的依赖。但令人高兴的是,六年后c++17std::any(和std::variant)添加到了该语言中。我最终使用std::any 拥有一个跨任意用户定义类型的 API。
  • David ValueStore 类的外观如何?
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    在您的情况下,您可能可以使用boost::any

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

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

    More info on a similar topic in this answer.

    【讨论】:

      【解决方案4】:

      首先,您不能拥有virtual 模板函数。由于模板在编译时解析,virtual 将不起作用,因为编译器不知道要选择哪个模板。有关更多信息,请参阅here

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

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

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

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

          【讨论】:

            猜你喜欢
            • 2016-09-28
            • 2014-10-25
            • 2021-06-16
            • 1970-01-01
            • 1970-01-01
            • 2016-04-12
            • 1970-01-01
            • 2011-11-28
            相关资源
            最近更新 更多