在派生模板类中用条件类型特征覆盖基类中的虚方法

问题描述:

任何人都可以解释为什么下面的代码在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; 
} 

[编辑]这不是一个重复的问题,因为条件性状方面的。

+2

一个好的技巧是始终声明函数'override'您打算要重写;那么如果你实际上没有覆盖任何东西,你将会遇到编译器错误 – Justin

+0

当你[添加'override'关键字](https://godbolt.org/g/ue7EGk)时,编译器很好的解释了为什么这样做不工作:“成员模板...可能没有virt-specifiers”。基本上,你不能混用虚函数和模板 – Justin

+2

[C++类的成员函数模板可以虚拟吗?](https://*.com/questions/2354210/can-ac-class-member-function- template-be-virtual) – Justin

问题是

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