Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Covariant return type and type conversion

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.
like image 465
template boy Avatar asked Jan 25 '15 03:01

template boy


People also ask

Which is a covariant return type?

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.

What is covariant datatype?

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++.

How is covariant return types implemented?

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.

What is the best possible reason to use covariant return types?

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.


1 Answers

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 of D::f differs from the return type of B::f, the class type in the return type of D::f shall be complete at the point of declaration of D::f or shall be the class type D. 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.

like image 104
Lightness Races in Orbit Avatar answered Oct 12 '22 04:10

Lightness Races in Orbit