Reputation: 1703
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
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());
}
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
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