Virtual inheritance & covariant return types

K

kikazaru

Is it possible to return covariant types for virtual methods inherite
from a base class using virtual inheritance

I've constructed an example below, which has the following structure

Shape = base clas
Triangle, Square = classes derived from Shap
Prism = class derived from Shap
TriangularPrism, SquarePrism = classes derived from Triangle an
Prism, or Square and Prism respectivel

Prism provides some functionality based on virtual methods specifie
in the Shape base class instantiated in specific Shapes Triangle an
Square. Since TriangularPrism and SquarePrism inherit Shape via Pris
AND Triangle/Square, it is necessary for Prism, Triangle and Square t
inherit Shape virtually

I added a virtual Shape* clone() function to Shape, and wanted t
return Triangle* for Triangle::clone(), SquarePrism* fo
SquarePrism::clone() etc. but this did not compile. I had to retur
Shape* for every version of clone(). This means that if I clone
SquarePrism, I have to use a dynamic cast to get at the method
specific to that class

Is this right!? Is it simply impossible to use covariant return type
for methods inherited virtually from a base class?!

----------------- Code example

#include "iostream

class Shape
public
virtual double area() = 0
virtual Shape* clone() = 0
virtual char* type() { return "Shape";
}

// Shape must be inherited virtually in order to resolve multipl
inheritance in SquarePris
class Square : virtual public Shape
double length

public
Square(double l) : length(l) {
virtual double area() { return length * length;
// Use of the covariant return type Square* causes a compilatio
error because Shape is inherited virtuall
// The error reads: "sorry, unimplemented: adjusting pointer
for covariant returns
// virtual Square *clone() { return new Square(*this);
virtual Shape *clone() { return new Square(*this);
virtual char* type() { return "Square";
}

// Shape must be inherited virtually in order to resolve multipl
inheritance in TriangularPris
class Triangle : virtual public Shape
double base, height

public
Triangle(double b, double h) : base(b), height(h) {
virtual double area() { return 0.5 * base * height;
// Use of the covariant return type Triangle* causes a compilatio
error because Shape is inherited virtuall
// The error reads: "sorry, unimplemented: adjusting pointer
for covariant returns
// virtual Triangle *clone() { return new Triangle(*this);
virtual Shape *clone() { return new Triangle(*this);
virtual char* type() { return "Triangle";
}

// Shape must be inherited virtually to resolve multiple inheritanc
in SquarePrism and TriangularPris
class Prism : virtual public Shape
double depth
public

Prism(double d) : depth(d) {
double volume() { return depth * area();
virtual char* type() { return "Prism";
}

class SquarePrism : public Square, public Prism
public
SquarePrism(double d, double l) : Square(l), Prism(d) {
virtual char* type() { return "SquarePrism";
// clone cannot return a covariant return type SquarePrism* for th
same reason as Square and Triangl
virtual Shape *clone() { return new SquarePrism(*this);
}

class TriangularPrism : public Triangle, public Prism
public
TriangularPrism(double d, double b, double h) : Triangle(b,h)
Prism(d) {
virtual char* type() { return "TriangularPrism";
// clone cannot return a covariant return type TriangularPrism* fo
the same reason as Square and Triangl
virtual Shape *clone() { return new TriangularPrism(*this);
}

using namespace std

int main()
Square s(1)
Triangle t(1,1)
SquarePrism sp(10, 1)
TriangularPrism tp(10, 1, 1)

cout << "Square area = " << s.area() <
endl
cout << "Triangle area = " << t.area(
<< endl
cout << "Cloned square area = " <
s.clone()->area() << endl
cout << "Cloned triangle area = " <
t.clone()->area() << endl

cout << "Square prism volume = " <
sp.volume() << endl
cout << "Triangular prism volume = " <
tp.volume() << endl
// Causes error because volume is not a function of Shap
// cout << "Cloned square prism volume = " <<
sp.clone()->volume() << endl;
// Use dynamic cast to allow access to the Prism functions:
cout << "Cloned square prism volume = " <<
(dynamic_cast<Prism*>(sp.clone()))->volume() << endl;

return 0;
}
 
A

Alf P. Steinbach

* kikazaru:
Is it possible to return covariant types for virtual methods inherited
from a base class using virtual inheritance?
Yes.


I've constructed an example below, which has the following structure:

Shape = base class
Triangle, Square = classes derived from Shape
Prism = class derived from Shape
TriangularPrism, SquarePrism = classes derived from Triangle and
Prism, or Square and Prism respectively

This seems to me an ungood hierarchy. A prism is a 3D shape, a triangle
is 2D shape. How can a prism be a triangle? Instead, I'd say a
triangular prism /has/ a triangle as one of its aspects. With that IMO
better design you avoid the multiple inheritance.

Prism provides some functionality based on virtual methods specified
in the Shape base class instantiated in specific Shapes Triangle and
Square. Since TriangularPrism and SquarePrism inherit Shape via Prism
AND Triangle/Square, it is necessary for Prism, Triangle and Square to
inherit Shape virtually.

I added a virtual Shape* clone() function to Sh7ape, and wanted to
return Triangle* for Triangle::clone(), SquarePrism* for
SquarePrism::clone() etc. but this did not compile. I had to return
Shape* for every version of clone(). This means that if I clone a
SquarePrism, I have to use a dynamic cast to get at the methods
specific to that class.

Is this right!? Is it simply impossible to use covariant return types
for methods inherited virtually from a base class?!

----------------- Code example:

#include "iostream"

Make that

class Shape {
public:
virtual double area() = 0;
virtual Shape* clone() = 0;
virtual char* type() { return "Shape"; }
};

Presumably at least the last two member functions should be 'const', and
with natural assumption of immutable objects, also the first.

// Shape must be inherited virtually in order to resolve multiple
inheritance in SquarePrism
class Square : virtual public Shape {
double length;

public:
Square(double l) : length(l) {}
virtual double area() { return length * length; }
// Use of the covariant return type Square* causes a compilation
error because Shape is inherited virtually
// The error reads: "sorry, unimplemented: adjusting pointers
for covariant returns"
// virtual Square *clone() { return new Square(*this); }
virtual Shape *clone() { return new Square(*this); }
virtual char* type() { return "Square"; }
};

The covariant version should compile just fine. Upgrade your compiler,
or use some other compiler. Even if you change the design as noted
above. If you can't change the design, and can't upgrade the compiler
and use some other compiler, then nothing prevents from localizing the
downcast in each class. Simply provide a member function cloneAsSquare.


[snip]
sp.clone()->volume() << endl;

Here you should have

cout << sp.clone()->volyume() << endl;

I guess you're aware that the code as-is leaks memory.
 
K

kikazaru

Thanks for your reply Alf, and your detailed comments. Sorry I didn'
post a response sooner, I didn't get a notification for some reaso
and assumed that no one had answered.

I have a couple of questions about what your wrote, but in answer t
your points

It is indeed a douple plus ungood hierarchy, I just made it up t
illustrate my problem and indeed, storing a Shape inside a Pris
instead of inheriting it would probably be better for this example
The same goes for the missing const specifiers.

Is that what you mean by "aspect"? -A data object inside
class

I wasn't really paying attention when I used "iostream".
usually use " symbols for my own headers and <> for syste
headers etc.

[color=blue:56eedcfdd8]Is there a convention about this? Does it mak
any practical difference?[/color:56eedcfdd8

I tried upgrading my compiler. I'm using g++ 3.3.5 on Debian Linux.
still get the same errors

sorry, unimplemented: adjusting pointers for covariant return

Also, I see that I could write specific clone functions, cloneAsSquar
etc. but this does not help when I want to clone a TriangularPrism o
a SquarePrism from a Prism*, then I'd need a cloneAsPrism functio
too. Maybe that's a fair price to pay but it feels like an excessiv
overhead.

I'm actually working on some 3D geometry and rendering stuff, wit
bigger classes that have enough data to want to avoid unnecessar
copying. In effect, I have classes derived from TriangularPrism an
SquarePrism and I want to copy construct these derived classes bu
also supply the TriangularPrism/Square prism parts to the cop
constructor so the SquarePrism/TriangularPrism parts can b
initialized differently to the copied data

The TriangularPrism/Square prism parts are built either by clone(
calls, or sometimes I build two at once and return
std::pair<Prism*, Prism*>. When I return the pair it seems
definately could not rely on covariant return types and return
std::pair<SquarePrism*, SquarePrism*> because this type is to
complicated for the compiler to recognize as a covariant retur
type

So I am in the situation where I have a pair of Prism* pointers,
know they actually point to SquarePrisms, and I want to clone a
object derived from SquarePrism but copy construct the SquarePris
part from one of these pointers to a different SquarePrism. The onl
way seems to be to dynamically cast them as SquarePrisms so they ca
be given to SquarePrism copy constructors in the constructor list fo
the class derived from SquarePrism
[color=blue:56eedcfdd8
What do you make of that?[/color:56eedcfdd8

And lastly yes, I wasn't really bothering about the memory leaks. I'
not sure I follow your correction though? Shouldn't I just delete th
cloned objects later by hand
 
P

pookiebearbottom

kikazaru said:
Is it possible to return covariant types for virtual methods inherited
from a base class using virtual inheritance?

yes it is possible/legal

it is implemented in your compiler for NON virtual inheritance.
Implemented in gcc starting with version 3.4

I know it is also broken in 7.0 and 7.1 MSVC, anyone know if it works
for 8.0/2005?

thanks

-sal
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top