在派生模板类中用条件类型特征覆盖基类中的虚方法
问题描述:
任何人都可以解释为什么下面的代码在Visual Studio 2015 C++中出现错误“错误C2259:'PropertyValue':无法实例化抽象类”?在派生模板类中用条件类型特征覆盖基类中的虚方法
编译器无法识别派生类PropertyValue
中的条件指定函数ConvertToDevice()
是否具有相同的签名?
非常感谢,
约翰
#include <type_traits>
#include <typeinfo>
class BasePropertyValue
{
public:
virtual int ConvertToDevice(void** ptrdObject) = 0;
};
template<typename T> class PropertyValue : public BasePropertyValue
{
public:
T value;
PropertyValue(T val)
{
value = val;
}
template<class Q = T>
typename std::enable_if<!std::is_pointer<Q>::value, int>::type ConvertToDevice(void** ptrdObject)
{
return 1;
}
template<class Q = T>
typename std::enable_if<std::is_pointer<Q>::value, int>::type ConvertToDevice(void** ptrdObject)
{
return 2;
}
};
void main()
{
PropertyValue<double>* prop1 = new PropertyValue<double>(20);
prop1->ConvertToDevice(nullptr);
double x = 20;
PropertyValue<double*>* prop2 = new PropertyValue<double*>(&x);
prop2->ConvertToDevice(nullptr);
return;
}
[编辑]这不是一个重复的问题,因为条件性状方面的。
答
问题是
template<class Q = T>
typename std::enable_if<!std::is_pointer<Q>::value, int>::type ConvertToDevice(void** ptrdObject)
{
return 1;
}
是模板方法不匹配(和不覆盖)在基类中的纯虚拟方法。
与其他SFINAE启用功能相同的问题。
因此PropertyValue
仍然是一个纯虚拟类,不能实例化。
一个可能的解决方案是创建一个中间基类,如下面的midClass
class BasePropertyValue
{ public: virtual int ConvertToDevice (void ** ptrdObject) = 0; };
template <typename T, bool = std::is_pointer<T>::value>
class midClass;
template <typename T>
class midClass<T, false> : public BasePropertyValue
{ public: int ConvertToDevice (void ** ptrdObject) override { return 1; } };
template <typename T>
class midClass<T, true> : public BasePropertyValue
{ public: int ConvertToDevice (void ** ptrdObject) override { return 2; } };
template <typename T>
class PropertyValue : public midClass<T>
{
public:
T value;
PropertyValue (T val)
{ value = val; }
};
答
首先,声明你试图重写为模板的功能。你不能有模板虚拟功能。就这么简单。
对于解决方案,您似乎已经使这些模板只是为了能够在两种实现之间切换。一个简单的解决办法是实现一个覆盖单一的功能,然后调用模板函数在它:
template<typename T>
struct PropertyValue : BasePropertyValue {
T value;
// simpler constructor
PropertyValue(T val) : value{std::move(val)} {}
// the override keyword is important
int ConvertToDevice(void** ptrdObject) override
{
return ConvertToDeviceImpl(ptrdobject);
}
private:
template<class Q = T>
typename std::enable_if<!std::is_pointer<Q>::value, int>::type
ConvertToDeviceImpl(void** ptrdObject)
{
return 1;
}
template<class Q = T>
typename std::enable_if<std::is_pointer<Q>::value, int>::type
ConvertToDeviceImpl(void** ptrdObject)
{
return 2;
}
};
+0
谢谢先生,这是完美的答案,并让我的一天。 – user7283981
一个好的技巧是始终声明函数'override'您打算要重写;那么如果你实际上没有覆盖任何东西,你将会遇到编译器错误 – Justin
当你[添加'override'关键字](https://godbolt.org/g/ue7EGk)时,编译器很好的解释了为什么这样做不工作:“成员模板...可能没有virt-specifiers”。基本上,你不能混用虚函数和模板 – Justin
[C++类的成员函数模板可以虚拟吗?](https://*.com/questions/2354210/can-ac-class-member-function- template-be-virtual) – Justin