s->duplicate()
returns an object of type Box*
, but I'm getting an error initializing it with Box*
. It looks like it's being converted back to Shape*
. What is the point of having covariant return types if it's converted back to the base class pointer?:
struct Shape
{
virtual Shape* duplicate()
{
return new Shape;
}
};
struct Box : Shape
{
virtual Box* duplicate()
{
return new Box;
}
};
int main()
{
Shape* s = new Box;
Box* b = s->duplicate();
}
Error:
main.cpp:22:12: error: cannot initialize a variable of type 'Box *' with an rvalue of type 'Shape *'
Box* b = s->duplicate();
^ ~~~~~~~~~~~~~~
1 error generated.
Covariant return type refers to return type of an overriding method. It allows to narrow down return type of an overridden method without any need to cast the type or check the return type. Covariant return type works only for non-primitive return types.
In object-oriented programming, a covariant return type of a method is one that can be replaced by a "narrower" type when the method is overridden in a subclass. A notable language in which this is a fairly common paradigm is C++.
When we override a parent class method, the name, argument types, and return type of the overriding method in child class has to be exactly the same as that of the parent class method. The overriding method was said to be invariant with respect to return type.
By using this feature, it is possible to override any method by changing the return type only. If the return type of overriding method in the subclass is a subtype of the declared return type of overridden method instead of being exactly the same type, it is known as covariant return types in Java.
Although Box::duplicate
is being invoked at runtime (via virtual dispatch), and although Box::duplicate
does override Shape::duplicate
(covariantly), and although Box::duplicate
does return a Box*
, you'll still get a Shape*
pointer because you are calling duplicate()
through a Shape*
pointer, and Shape*
is the return type of Shape::duplicate()
, and the compiler only sees you calling Shape::duplicate
, not Box::duplicate
.
C++ is not able to dynamically select types, so this is the best it can do. Your Box*
is being automatically converted to a Shape*
on the way out of Box::duplicate
. As Barry said, "it still has to compile at compile time, and at compile time all we know is that it returns a Shape*
".
Then, to make it into a Box*
again, you need to explicitly cast it (using static_cast
or dynamic_cast
) because no implicit down-conversion exists.
[C++11: 10.3/7]:
The return type of an overriding function shall be either identical to the return type of the overridden function or covariant with the classes of the functions. [..]
[C++11: 10.3/8]:
If the return type ofD::f
differs from the return type 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
. When the overriding function is called as the final overrider of the overridden function, its result is converted to the type returned by the (statically chosen) overridden function (5.2.2). [..]
In the standard text, a pertinent example follows.
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