By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
454,925 Members | 1,134 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 454,925 IT Pros & Developers. It's quick & easy.

Virtual inheritance & covariant return types

P: n/a
Is it possible to return covariant types for virtual methods inherited
from a base class using virtual inheritance?

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

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 Shape, 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"

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 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"; }
};

// Shape must be inherited virtually in order to resolve multiple
inheritance in TriangularPrism
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 compilation
error because Shape is inherited virtually
// The error reads: "sorry, unimplemented: adjusting pointers
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 inheritance
in SquarePrism and TriangularPrism
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 the
same reason as Square and Triangle
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* for
the same reason as Square and Triangle
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 Shape
// 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;
}

Jul 12 '06 #1
Share this Question
Share on Google+
3 Replies


P: n/a
* 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

#include <iostream>

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.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 12 '06 #2

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

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

It is indeed a douple plus ungood hierarchy, I just made it up to
illustrate my problem and indeed, storing a Shape inside a Prism
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 a
class?

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

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

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

sorry, unimplemented: adjusting pointers for covariant returns

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

I'm actually working on some 3D geometry and rendering stuff, with
bigger classes that have enough data to want to avoid unnecessary
copying. In effect, I have classes derived from TriangularPrism and
SquarePrism and I want to copy construct these derived classes but
also supply the TriangularPrism/Square prism parts to the copy
constructor so the SquarePrism/TriangularPrism parts can be
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 a
std::pair<Prism*, Prism*>. When I return the pair it seems I
definately could not rely on covariant return types and return a
std::pair<SquarePrism*, SquarePrism*because this type is too
complicated for the compiler to recognize as a covariant return
type?

So I am in the situation where I have a pair of Prism* pointers, I
know they actually point to SquarePrisms, and I want to clone an
object derived from SquarePrism but copy construct the SquarePrism
part from one of these pointers to a different SquarePrism. The only
way seems to be to dynamically cast them as SquarePrisms so they can
be given to SquarePrism copy constructors in the constructor list for
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'm
not sure I follow your correction though? Shouldn't I just delete the
cloned objects later by hand?

Jul 13 '06 #3

P: n/a
kikazaru wrote:
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

Jul 31 '06 #4

This discussion thread is closed

Replies have been disabled for this discussion.