C++ 虚拟模板方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7968023/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ Virtual template method
提问by Vincent
I have an abstract class (I know that it will not compile this way, but it's for comprehension of what I want to do) :
我有一个抽象类(我知道它不会以这种方式编译,但它是为了理解我想要做什么):
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);
};
So when I call setData<double>("foodouble", data)
I want the double identified by foodouble
(internal mechanism which is not the main concern here) to be set to the double data.
因此,当我打电话时,setData<double>("foodouble", data)
我希望将foodouble
(内部机制,这不是这里主要关注的)标识的双精度设置为双精度数据。
So how to do that?
那么怎么做呢?
I think that there may be a mean by typing something like virtual void setData<double>(std::string id, double data)
but I don't know how to do it.
我认为通过输入类似的东西可能有一种手段,virtual void setData<double>(std::string id, double data)
但我不知道该怎么做。
回答by David Rodríguez - dribeas
The problem is that you cannot mix static time polymorphism (templates) with runtime polymorphism easily. The reason for the language disallowing the particular construct in your example is that there are potentially infinite different types that could be instantiating your template member function, and that in turn means that the compiler would have to generate code to dynamically dispatch those many types, which is infeasible.
问题是您不能轻易地将静态时间多态性(模板)与运行时多态性混合在一起。语言不允许在您的示例中使用特定构造的原因是,可能有无限种不同的类型可以实例化您的模板成员函数,这反过来意味着编译器必须生成代码来动态分派这些许多类型,这是不可行的。
There are different things that can be done here to get around the limitation, basically either take away the static or the dynamic polymorphism. Removing dynamic polymorphism from the equation could be done by providing a type that is not derived from, to store the <key,value>
mappings, and then offering the template that resolves that only at the base level:
这里可以做不同的事情来绕过这个限制,基本上要么去掉静态多态性,要么去掉动态多态性。可以通过提供一个不是派生自的类型来存储<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;
};
Now deriving classes can access the ValueStore
from the base and there is no need for polymorphism. (This can also be done by implementing the functionality directly in AbstractComputation
but it probably makes sense to separate concerns)
现在派生类可以ValueStore
从基类访问并且不需要多态性。(这也可以通过直接实现功能来完成,AbstractComputation
但分离关注点可能是有意义的)
The other option is to maintain runtime polymorphism, but remove static polymorphism. This can be done by performing type erasure on the base class and then dispatching to the appropriate (non-templated) function that takes the type-erasedarguments. The simplest version of this is just using boost::any
:
另一种选择是保持运行时多态性,但删除静态多态性。这可以通过对基类执行类型擦除然后分派到采用类型擦除参数的适当(非模板化)函数来完成。最简单的版本就是使用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;
};
How type erasure is implemented under the hood is interesting, but out of the scope here, the important part is that a boost::any
is a concrete (non-templated) type that can store anytype internally by using type erasure on the arguments, and at the same time allows for type-safe retrieval of the data.
如何在幕后实现类型擦除很有趣,但在这里的范围之外,重要的部分是 aboost::any
是一个具体的(非模板化)类型,它可以通过在参数上使用类型擦除在内部存储任何类型,并且在同时允许对数据进行类型安全的检索。
回答by TobiMcNamobi
In some cases it may be enough to move the templating from method level to class level, e.g.:
在某些情况下,将模板从方法级别移动到类级别可能就足够了,例如:
#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;
}
回答by jpalecek
You can probably use boost::any
in your case.
您可能可以boost::any
在您的情况下使用。
virtual void setData(std::string id, boost::any data);
It is a wrapper that can encapsulate almost anything.
它是一个包装器,几乎可以封装任何东西。
回答by Tony The Lion
回答by Nim
Use boost::any
to accept the datum, and then when you actually set, grab the correct type from it.
使用boost::any
接受数据,然后当你真正设置,从中抓住正确的类型。
回答by user396672
If you know list of possible types in advance, preprocessor may help:
如果您事先知道可能的类型列表,预处理器可能会有所帮助:
#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
};
If you don't know a complete list of possible types, perhaps, your problem is unresolvable. Type erasure, as mentioned by others, may also help.. but not in all circumstances.
如果您不知道可能类型的完整列表,那么您的问题可能无法解决。正如其他人提到的那样,类型擦除也可能有所帮助......但并非在所有情况下。