I have a class hierarchy with multiple levels of inheritance.
cloneabledeclares a pure virtual member function returningcloneable *.basederives fromcloneable, but does not declare any member functions.- Finally,
derivedderives frombaseand defines the virtual function, but overrides the return type toderived *.
Calling the virtual function via a base pointer to derived object returns cloneable *. I was expecting base * because the implementation of the virtual function returns derived * which is convertible to base *. What is going on here?
If I declare the pure virtual function in base, I can finally get base * from it, but I do not understand why this declaration is necessary.
Code:
struct cloneable
{
virtual cloneable * clone() = 0;
};
struct base : cloneable
{
// virtual base * clone() = 0; // this line resolves the compile error
};
struct derived : base
{
virtual derived * clone () { return new derived; }
};
int main(int, char**)
{
derived d;
base * bp = &d;
base * bbp = bp->clone(); // error: invalid conversion
// from ‘cloneable*’ to ‘base*’
return 0;
}
Note: I’ve deliberately omitted the virtual destructor to shorten the code example.
Exactly how do you think the compiler should guess that you want a version returning a
base*, without any declaration?While the above question answers your direct question, I feel that I should also add some advice.
First of all,
clonefunctionconst, so that it can be called on aconstobject or via an rvalue expression.I.e.,
Secondly, to create a clone of an object,
new T( *this )(using copy constructor), notnew T(using default constructor).And third,
cloneoperation return a smart pointer such as aunique_ptr<MyClass>, not a raw pointer.However, with the change of return type to smart pointer, you will no longer benefit directly from the C++ support for covariant function results, which is only for raw pointers and references. So one way to do that is to have a non-
publicraw pointer result implementation, which can have covariant result type, and simply a typedpublicwrapper that returns a smart pointer. In effect you’re then implementing the covariance for the public interface, yourself, and it can look like this: