C++ Virtual template method
我有一个抽象类(我知道它不会以这种方式编译,但它是为了理解我想做什么):
1 2 3 4 5 6 7 8 9 10 11 | 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); }; |
所以当我调用
那么怎么做呢?
我认为输入类似于
问题是,不能轻易地将静态时间多态性(模板)与运行时多态性混合在一起。该语言不允许在示例中使用特定构造的原因是可能存在无限多的不同类型,这些类型可以实例化模板成员函数,而这又意味着编译器必须生成代码来动态地调度这些类型,这是不可行的。
这里可以做不同的事情来绕过这个限制,基本上要么去掉静态多态性,要么去掉动态多态性。从方程中删除动态多态性可以通过提供一个不是从中派生的类型来完成,以存储
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; }; |
现在派生类可以从基础访问
另一种选择是维护运行时多态性,但删除静态多态性。这可以通过在基类上执行类型擦除,然后分派到接受类型擦除参数的相应(非模板化)函数来完成。最简单的版本是使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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; }; |
类型擦除是如何在引擎盖下实现的是有趣的,但在这里,重要的部分是
在某些情况下,将模板从方法级别移动到类级别可能就足够了,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #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; } |
首先,您不能使用
如果预先知道可能的类型列表,预处理器可能会帮助:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #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 }; |
如果您不知道可能的类型的完整列表,那么您的问题可能无法解决。如其他人提到的,类型擦除也可能有帮助。但并非在所有情况下。
您可以在您的案例中使用
1 | virtual void setData(std::string id, boost::any data); |
它是一种可以封装几乎任何东西的包装器。
有关此答案中类似主题的详细信息。
使用