0

Can anyone explain why the code below gives the error "error C2259: 'PropertyValue': cannot instantiate abstract class" in Visual Studio 2015 C++?

Is the compiler not able to identify that the conditionally specified function ConvertToDevice() in the derived class PropertyValue has the same signature?

Many thanks,

John

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

[edit] This is not a duplicate question because of the conditional traits aspect.

  • 2
    A good tip is to always declare functions `override` that you intend to be overriding; then you will have a compiler error if you actually didn't override anything – Justin Sep 11 '17 at 20:37
  • When you [add the `override` keyword](https://godbolt.org/g/ue7EGk), the compiler does a good job explaining why this doesn't work: "member template ... may not have virt-specifiers". Basically, you can't mix virtual functions and templates – Justin Sep 11 '17 at 20:41
  • 2
    Possible duplicate of [Can a C++ class member function template be virtual?](https://stackoverflow.com/questions/2354210/can-a-c-class-member-function-template-be-virtual) – Justin Sep 11 '17 at 20:43
  • Also: https://stackoverflow.com/q/13818463/1896169 – Justin Sep 11 '17 at 20:43

2 Answers2

2

First, you declare the function you're trying to override as templates. You cannot have template virtual function. It's as simple as that.

For for the solution, you seem to have made those templates just to be able to switch between two implementation. A simple solution would be to implement a single function that overrides, and then call a template function in it:

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;
    }
};
Guillaume Racicot
  • 32,627
  • 7
  • 60
  • 103
1

The problem is that

template<class Q = T>
typename std::enable_if<!std::is_pointer<Q>::value, int>::type ConvertToDevice(void** ptrdObject)
{
    return 1;
}

is a template method that doesn't match (and not override) the pure virtual method in the base class.

Same problem with the other SFINAE enabled function.

So PropertyValue remain a pure virtual class and can't be instantiated.

A possible solution is to create a intermediate base class, as the following 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; }
 };
max66
  • 60,491
  • 9
  • 65
  • 95