john_smith_lon
john_smith_lon

Reputation: 149

Overriding a virtual method in a base class with conditional type traits in derived template class

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.

Upvotes: 0

Views: 1116

Answers (2)

Guillaume Racicot
Guillaume Racicot

Reputation: 41800

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

Upvotes: 2

max66
max66

Reputation: 66230

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

Upvotes: 1

Related Questions