I have a quite complex class hierarchy in which the classes are cross-like depending on each other: There are two abstract classes A and C containing a method that returns an instance of C and A, respectively. In their inherited classes I want to use a co-variant type, which is in this case a problem since I don't know a way to forward-declare the inheritance relation ship.
I obtain a "test.cpp:22: error: invalid covariant return type for ‘virtual D* B::outC()’"-error since the compiler does not know that D is a subclass of C.
class C;
class A {
public:
virtual C* outC() = 0;
};
class C {
public:
virtual A* outA() = 0;
};
class D;
class B : public A {
public:
D* outC();
};
class D : public C {
public:
B* outA();
};
D* B::outC() {
return new D();
}
B* D::outA() {
return new B();
}
If I change the return type of B::outC() to C* the example compiles. Is there any way to keep B* and D* as return types in the inherited classes (it would be intuitive to me that there is a way)?
1) Covariant return type assists to stay away from the confusing type casts in the class hierarchy and makes the code more usable, readable, and maintainable. 2) In the method overriding, the covariant return type provides the liberty to have more to the point return types.
Covariant return type works only for non-primitive return types. From Java 5 onwards, we can override a method by changing its return type only by abiding the condition that return type is a subclass of that of overridden method return type.
Covariant Method overriding means that when overriding a method in the child class, the return type may vary. Before java 5 it was not allowed to override any function if the return type is changed in the child class. But now it is possible only return type is subtype class.
C++'s classical OOP system supports “covariant return types,” but it does not support “contravariant parameter types.”
I know of no way of having directly coupled covariant members in C++. You'll have either to add a layer, or implement covariant return yourself.
For the first option
class C;
class A {
public:
virtual C* outC() = 0;
};
class C {
public:
virtual A* outA() = 0;
};
class BI : public A {
public:
};
class D : public C {
public:
BI* outA();
};
class B: public BI {
public:
D* outC();
};
D* B::outC() {
return new D();
}
BI* D::outA() {
return new B();
}
and for the second
class C;
class A {
public:
C* outC() { return do_outC(); }
virtual C* do_outC() = 0;
};
class C {
public:
virtual A* outA() = 0;
};
class D;
class B : public A {
public:
D* outC();
virtual C* do_outC();
};
class D : public C {
public:
B* outA();
};
D* B::outC() {
return static_cast<D*>(do_outC());
}
C* B::do_outC() {
return new D();
}
B* D::outA() {
return new B();
}
Note that this second option is what is done implicitly by the compiler (with some static checks that the static_cast is valid).
As far as I know, there's no way to do this without explicit casting. The problem is that the definition of class B
can't know that D
is a subclass of C
until it sees a full definition of class D
, but the definition of class D
can't know that B
is a subclass of A
until it sees a full definition of class B
, and so you have a circular dependency. This can't be resolved with forward-declarations because a forward declaration unfortunately cannot specify an inheritance relationship.
There's a similar problem with trying to implement a covariant clone()
method using templates, which I found can be solved, but the analogous solution still fails here because the circular reference remains impossible to resolve.
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