Reputation: 791
I'm not exactly sure what to call this inheritance scheme, but I'm trying to use a cloneable
interface with a default implementation. I'm having some issues getting the correct scheme though.
I'm basing this somewhat on the cloneable interface defined in C#.
First I have my interface and default implementations:
template<class BaseType>
class ICloneable
{
public:
virtual std::shared_ptr<BaseType> Clone() const = 0;
};
template<class BaseType, class DerivedType>
class Cloneable : public ICloneable<BaseType>
{
public:
virtual std::shared_ptr<BaseType> Clone() const
{
return std::shared_ptr<BaseType>(new DerivedType(*(DerivedType*)this));
}
};
My desire is to have the following scheme.
// A pure virtual base interface
class Base : public ICloneable<Base>
{
public:
virtual void SomeFunc() = 0;
}
// Another implementation
class Imp1 : public Base, Cloneable<Base, Imp1>
{
public:
virtual void SomeFunc() {}
}
// An implementation
class Imp2 : public Cloneable<Base, Imp2>
{
public:
virtual void SomeFunc() {}
}
If I have a list of `std::shared_ptr' objects, I can invoke the Clone function when I want to make a deep copy without having to manually write the function in every one of the implementations.
Right now I get that Imp is an abstract class, which doesn't surprise me. Anyone know how I can get this default implementation idea to work? The point is to not have to manually write the clone function for every one of the implementations. It might not be doable but I'm out of ideas to try.
Upvotes: 3
Views: 2227
Reputation: 2558
You could do the following:
#include <memory>
template<typename InterfaceType_>
struct ICloneable
{
using InterfaceType = InterfaceType_;
virtual ~ICloneable() = default;
virtual std::shared_ptr<InterfaceType> clone() const = 0;
};
template<typename T, typename Base = ICloneable<T>>
struct CloneableMixin : public Base
{
using InterfaceType = typename Base::InterfaceType;
// With the following line uncommented, code does not compile in MSVC
//using typename Base::InterfaceType;
std::shared_ptr<InterfaceType> clone() const override
{ return std::make_shared<T>(*static_cast<const T*>(this)); }
};
Now, this can be used as follows:
struct SomeBaseClass : public CloneableMixin<SomeBaseClass> { /*...*/ };
struct SomeDerivedClass : public CloneableMixin<SomeDerivedClass, SomeBaseClass> { /*...*/ };
Two notes:
In order to be able to access InterfaceType_
template parameter of ICloneable
, you need to make it a template alias, and then use using typename Base::InterfaceType
(as it is template parameter dependent type).
I've provided default type for Base
template parameter of CloneableMixin
- this allows to use it for base classes, for which you want to have clone
implemented.
Moreover, two unrelated comments:
You don't need to type virtual
- it's implied. It's a good idea to add override
at the end (this makes sure that the method actually overrides something, otherwise a compiler will report an error).
You might consider using std::make_shared
instead of new
.
Upvotes: 3