Erunehtar
Erunehtar

Reputation: 1703

Template class override base class virtual function

Consider the following code:

class Base
{
public:
    virtual void* allocate(){ return nullptr; }
};

template <class T> class BaseTemplate : public Base
{
public:
    void* allocate() override { return new T(); }
};

class IntSpecialization : public BaseTemplate<int>
{
};

Base GetSpecialization(const int&){ return IntSpecialization(); }

The goal is to be able to use template to implement specializations, but still allow users to work using the base class interface, such as:

int a;
auto s = GetSpecialization(a);
auto p = s.allocate();

The above code does not work; s.allocate() always return nullptr for obvious reasons.

I absolutely need the GetSpecialization function to return the Base non-template class, so how do I go about this?

The Base class virtual method cannot be pure, because otherwise it becomes abstract and it will fail the compilation at GetSpecialization.

What is the best approach to solve this pattern? Using C++11? Thanks!

Upvotes: 2

Views: 2801

Answers (2)

Praetorian
Praetorian

Reputation: 109289

Base GetSpecialization(const int&){ return IntSpecialization(); }

You're slicing the IntSpecialization object above. To make your code work, GetSpecialization must return a Base *, or a Base&. For instance, the following will work as you intended it to:

std::unique_ptr<Base> GetSpecialization(const int&)
{ 
    return std::unique_ptr<Base>(new IntSpecialization()); 
}

Live demo

For the above code to work, you'll need to add a virtual destructor to Base.

class Base
{
public:
    virtual void* allocate(){ return nullptr; }
    virtual ~Base() = default;
};

Otherwise, when the unique_ptr goes out of scope it'll call delete ptr;, where the type of ptr is Base *, and polymorphic deletion of a derived class object through a base class pointer is undefined behavior unless the base class destructor is virtual.

Upvotes: 4

Slava
Slava

Reputation: 44278

Just make Base have pointer to BaseTemplate:

class BaseInterface {
public:
    virtual void* allocate() = 0;
}

class Base
{
   std::unique_ptr<BaseInterface> interface;
public:
    Base( BaseInterface *i ) : interface( i ) {}
    void* allocate(){ return interface->allocate(); }
};

template <class T> class BaseTemplate : public BaseInterface
{
public:
    void* allocate() override { return new T(); }
};

class IntSpecialization : public BaseTemplate<int>
{
};

Base GetSpecialization(const int&){ return Base( new IntSpecialization ); }

Less verbose solution is to use std::function and lambda

class Base 
{
public:
    typedef std::function<void *()> creator;
    Base( const creator &c ) : cr( c ) {}
    void *allocate() { return cr(); }
private:
    creator cr;
};

template<class T>
Base GetSpecialization( const T & ) { return Base( []() { return new T; } ); }

Upvotes: 0

Related Questions