Here is my case:
class A
{
public:
virtual A* clone() = 0;
};
class B : public A
{
virtual B* clone() override
{
return new B();
}
};
In my code now 100% of sublcasses of A
just implement the clone
method exactly the same way only for class D
I have return type D
and I create object of D
of course.
How can I prevent this duplication? What patterns or tricks can help?
I have thought to use CRTP pattern, but it uses templates, and my function is virtual. As we understand templates and virtual functions are incompatible.
NVI pattern does not work either, as return type of clone methods are different.
[class.virtual]/8:
If the class type in the covariant return type of
D::f
differs from that ofB::f
, the class type in the return type ofD::f
shall be complete at the point of declaration ofD::f
or shall be the class typeD
.
So CRTP cannot work perfectly, since the template argument which refers to the derived class would be an incomplete type, and thus covariant return types are difficult to simulate.
However, here's a try:
class A
{
public:
virtual A* clone() = 0;
};
namespace detail {
template <typename T>
struct Cloner : A {
using A::A; // For constructors
virtual A* clone() override {return new T;}
};
}
template <typename T>
struct Cloner : detail::Cloner<T> {
using detail::Cloner<T>::Cloner; // For constructors
// For callers
template <class=void>
T* clone() {return static_cast<T*>(detail::Cloner<T>::clone());}
};
class B : public Cloner<B>
{
};
Demo.
Note two things:
The overload of clone
returning a pointer to the derived class type is a function template. That's so we don't override the virtual function clone
: If we would, the code would be ill-formed, because the return type is not covariant (see above).
Because clone
is a function template, to ensure that it get's called at all, we can to let it hide the virtual clone
function. That's achieved via inheritance: Names in derived classes hide names in base classes. Thus if we call through a B
-pointer/reference, we get the appropriate return type (B*
), since the name is looked up in ::Cloner
before detail::Cloner
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With