Covariant return types doesn't work (with g++ 4.1.2)

M

mr.xiaofan.li

Hi,

I have been totally confused about the covariant return type feature
in C++. Below is an example code I wrote that doesn't compile with g++
4.1.2 (on Fedora 8)

class virt_base
{
public:
virt_base()
{
}

virtual ~virt_base()
{

}

virtual virt_base* cut()
{
return new virt_base();
}

void say_hi()
{
cout <<"hi!!! " <<endl;
}
};

class virt_derived
: public virt_base
{
public:
virt_derived()
{
}

~virt_derived()
{

}

virtual virt_derived* cut()
{
return new virt_derived();
}

void say_hi()
{
cout <<"HI!!!! " <<endl;
}
};

int main()
{
virt_base* my_virt_derived = new virt_derived();
virt_derived* new_virt_derived = my_virt_derived->cut(); // g++
complains here: invalid
//
conversion from 'virt_base*' to
//
'virt_derived*'
new_virt_derived->say_hi();
}
 
L

Leon

Hi,

I have been totally confused about the covariant return type feature in
C++. Below is an example code I wrote that doesn't compile with g++
4.1.2 (on Fedora 8)

class virt_base
{
public:
virt_base()
{
}

virtual ~virt_base()
{

}

virtual virt_base* cut()
{
return new virt_base();
}

void say_hi()
{
cout <<"hi!!! " <<endl;
}
};

class virt_derived
: public virt_base
{
public:
virt_derived()
{
}

~virt_derived()
{

}

virtual virt_derived* cut()
{
return new virt_derived();
}

void say_hi()
{
cout <<"HI!!!! " <<endl;
}
};

int main()
{
virt_base* my_virt_derived = new virt_derived(); virt_derived*
new_virt_derived = my_virt_derived->cut(); // g++
complains here: invalid
//
conversion from 'virt_base*' to
//
'virt_derived*'
new_virt_derived->say_hi();
}

try changing your main() to this:

int main()
{
virt_base* my_virt_derived = new virt_derived();
virt_derived* new_virt_derived = (virt_derived*)my_virt_derived-
}
 
A

Alf P. Steinbach

* (e-mail address removed):
I have been totally confused about the covariant return type feature
in C++. Below is an example code I wrote that doesn't compile with g++
4.1.2 (on Fedora 8)

class virt_base

Note that the term "virtual base (class)" has a special meaning in C++.

Your class "virt_base" is not used as a virtual base class.

{
public:
virt_base()
{
}

virtual ~virt_base()
{

}

virtual virt_base* cut()
{
return new virt_base();
}

void say_hi()
{
cout <<"hi!!! " <<endl;
}
};

class virt_derived
: public virt_base
{
public:
virt_derived()
{
}

~virt_derived()
{

}

virtual virt_derived* cut()
{
return new virt_derived();
}

void say_hi()
{
cout <<"HI!!!! " <<endl;
}
};

int main()
{
virt_base* my_virt_derived = new virt_derived();
virt_derived* new_virt_derived = my_virt_derived->cut(); // g++
complains here: invalid
//
conversion from 'virt_base*' to
//
'virt_derived*'

Please be a little more conscientious with your quoting. This code
can't be copied and compiled. It must be manually reformatted.

The problem is simply that virt_base::cut() returns a virt_base*.

So you're initializing a virt_derived* pointer with a virt_base* pointer.

Declare new_virt_derived as

virt_base* new_virt_derived = ...

In addition, to get the result I think you expect, you'll have to
declare say_hi as virtual in virt_base.

new_virt_derived->say_hi();
}


Cheers, & hth.,

- Alf
 
A

Alf P. Steinbach

* Leon:
try changing your main() to this:

int main()
{
virt_base* my_virt_derived = new virt_derived();
virt_derived* new_virt_derived = (virt_derived*)my_virt_derived-

To the OP:

Don't follow that advice.

Casts can often make code compile, at the expense of most often being
incorrect, and hiding future errors.

The cast says "I know what I'm doing", when that is absolutely not so.

In short the above is extremely bad advice: it is advicing you to lie
and bluff your way.

Don't pay any heed to folks who think a cast (and especially a C style
cast) is a solution.

They simply don't know anything about what they're talking about.



Cheers, & hth.,

- Alf
 
T

Thomas J. Gritzan

class virt_base
{ [...]
virtual virt_base* cut()
{
return new virt_base();
} [...]
};

class virt_derived
: public virt_base
{ [...]
virtual virt_derived* cut()
{
return new virt_derived();
} [...]
};

The covariant return type lets you override a function with a more strict
function, i.e. a function returning a derived type. However, type checking
is done at compile time, so your compiler has to know, that the object you
are calling cut() on is a virt_derived.
int main()
{
virt_base* my_virt_derived = new virt_derived();

Change this to
virt_derived* my_virt_derived = new virt_derived();
 
L

Leon

* Leon:

To the OP:

Don't follow that advice.

Casts can often make code compile, at the expense of most often being
incorrect, and hiding future errors.

The cast says "I know what I'm doing", when that is absolutely not so.

In short the above is extremely bad advice: it is advicing you to lie
and bluff your way.

Don't pay any heed to folks who think a cast (and especially a C style
cast) is a solution.

They simply don't know anything about what they're talking about.




Cheers, & hth.,

- Alf

You think casts are useless? Then I want you to read some integers from a
fstream, since fstream's read method returns chars only. In some cases
you are right, and is a cast stupid. But maybe it works for this case.
And if i say something wrong, please explain me my mistake, and don't
respond so unkind. Or do you want to say you never make mistakes?

Grz, Leon
 
A

Alf P. Steinbach

* Thomas J. Gritzan:
class virt_base
{ [...]
virtual virt_base* cut()
{
return new virt_base();
} [...]
};

class virt_derived
: public virt_base
{ [...]
virtual virt_derived* cut()
{
return new virt_derived();
} [...]
};

The covariant return type lets you override a function with a more
strict function, i.e. a function returning a derived type. However, type
checking is done at compile time, so your compiler has to know, that the
object you are calling cut() on is a virt_derived.
int main()
{
virt_base* my_virt_derived = new virt_derived();

Change this to
virt_derived* my_virt_derived = new virt_derived();

That would make the code compile, but would defeat the pupose of the code.

Cheers,

- Alf
 
T

Thomas J. Gritzan

Alf said:
* Thomas J. Gritzan:
class virt_base
{ [...]
virtual virt_base* cut()
{
return new virt_base();
} [...]
};

class virt_derived
: public virt_base
{ [...]
virtual virt_derived* cut()
{
return new virt_derived();
} [...]
};

The covariant return type lets you override a function with a more
strict function, i.e. a function returning a derived type. However,
type checking is done at compile time, so your compiler has to know,
that the object you are calling cut() on is a virt_derived.
int main()
{
virt_base* my_virt_derived = new virt_derived();

Change this to
virt_derived* my_virt_derived = new virt_derived();

That would make the code compile, but would defeat the pupose of the code.

That would be with your code change:

virt_base* my_virt_derived = new virt_derived();
virt_base* new_virt_derived = my_virt_derived->cut();

No need for covariant return types, since both pointers are virt_base* and
virt_base::cut returns virt_base*.

Since we want to show how to use covariant return types, we have to declare
both pointers as virt_derived*.

I guess one of us needs a cup of coffee again :)
 
L

Leon

You think casts are useless? Then I want you to read some integers from
a fstream, since fstream's read method returns chars only. In some cases
you are right, and is a cast stupid. But maybe it works for this case.
And if i say something wrong, please explain me my mistake, and don't
respond so unkind. Or do you want to say you never make mistakes?

Grz, Leon

By the way, this was to mr. Alf P. Steinbach, not to anyone else.
 
A

Alf P. Steinbach

* Leon -> Alf P. Steinbach:
You think casts are useless?

No, they're in the language for a reason.

That reason is not to make code that one does not understand, compile.

Then I want you to read some integers from a
fstream, since fstream's read method returns chars only.

stream >> myInt;

Of course it may be that you're talking about binary i/o, since you
mention read().

In that case, consider whether binary i/o really is what you need. Most
often it's not.

In some cases
you are right, and is a cast stupid.

Most often, a cast is stupid. But the reason for that isn't that casts
are inherently bad. The reason is that they're so often abused.

But maybe it works for this case.

When it "works" technically it's even worse than when it clearly
doesn't, because it can then give a false impression of doing something
right, and so easy too... Leading to much worse problems down the road.

And if i say something wrong, please explain me my mistake,

Already done.

Simply don't recommend casts as solutions. They're generally not. If
one wants less type checking then the appropriate solution is to use a
language with even less strict type checking, and/or not defining types.

and don't
respond so unkind. Or do you want to say you never make mistakes?

I would want to say that, yes. :) Unfortunately, the road to
perfection is longer than my estimated lifetime. But who wouldn't want
to live that long?


Cheers, & hth.,

- Alf
 
L

Leon

* Leon -> Alf P. Steinbach:

No, they're in the language for a reason.

That reason is not to make code that one does not understand, compile.



stream >> myInt;

Of course it may be that you're talking about binary i/o, since you
mention read().

In that case, consider whether binary i/o really is what you need. Most
often it's not.



Most often, a cast is stupid. But the reason for that isn't that casts
are inherently bad. The reason is that they're so often abused.



When it "works" technically it's even worse than when it clearly
doesn't, because it can then give a false impression of doing something
right, and so easy too... Leading to much worse problems down the road.



Already done.

Simply don't recommend casts as solutions. They're generally not. If
one wants less type checking then the appropriate solution is to use a
language with even less strict type checking, and/or not defining types.



I would want to say that, yes. :) Unfortunately, the road to
perfection is longer than my estimated lifetime. But who wouldn't want
to live that long?


Cheers, & hth.,

- Alf

Okay, thank you for the info. I will remember it :)

Grz, Leon
 
A

Alf P. Steinbach

* Thomas J. Gritzan:
Alf said:
* Thomas J. Gritzan:
(e-mail address removed) wrote:
class virt_base
{
[...]
virtual virt_base* cut()
{
return new virt_base();
}
[...]
};

class virt_derived
: public virt_base
{
[...]
virtual virt_derived* cut()
{
return new virt_derived();
}
[...]
};

The covariant return type lets you override a function with a more
strict function, i.e. a function returning a derived type. However,
type checking is done at compile time, so your compiler has to know,
that the object you are calling cut() on is a virt_derived.

int main()
{
virt_base* my_virt_derived = new virt_derived();

Change this to
virt_derived* my_virt_derived = new virt_derived();

That would make the code compile, but would defeat the pupose of the
code.

That would be with your code change:

virt_base* my_virt_derived = new virt_derived();
virt_base* new_virt_derived = my_virt_derived->cut();

No need for covariant return types, since both pointers are virt_base*
and virt_base::cut returns virt_base*.

Since we want to show how to use covariant return types, we have to
declare both pointers as virt_derived*.

I guess one of us needs a cup of coffee again :)

Heh.

I seems I need to stop pointing out my own mistakes (like the coffee
thing), lest people think that someone who does that is mistaken all the
time or most of the time -- but I refuse to cater to such perceptions.

You're partially right. There are two things to show. First, that that
covariant function works as an override (needing a virt_base* to show
that), which I consider primary, otherwise it wouldn't need to be
virtual, and your suggestion doesn't call the virtuality into play.
Second, that when the static type my_virt_derived is known the covariant
function provides a way to avoid casting, and your suggestion does that.


Cheers,

- Alf
 
J

Jeff Schwab

Hi,

I have been totally confused about the covariant return type feature
in C++.

.... snip code along these lines:

struct base {
virtual ~base() { }
virtual base* cut() { return new base; }
};

struct derived: base {
virtual derived* cut() { return new derived; }
};

The covariant return type of the cut() method allows the derived class
to override the method, even though it actually returns a different,
more specialized static type. If you were not allowed to return a
covariant type, then derived::cut() would have to return a
pointer-to-base, even when the method was invoked directly on an object
of derived type. In other words, you would have to write code like this:

derived d;
derived* pd = static_cast<derived*>(pd->cut());

Thanks to the covariant return type, you can write much cleaner code:

derived d;
derived* pd = pd->cut();

In your example, your client code looked something like this:

int main() {
base* b = new derived();
derived* d = b->cut(); // error
}

The problem here is that you have told the compiler to forget the
dynamic type of the first dynamically allocated object, but you have
then tried to rely on knowledge of that type. Leon pointed out that you
can tell the compiler that you know what you're doing by down-casting
the pointer; however, Alf wisely suggested that doing so defeats the
purpose of using a language like C++.

If you allocate an object of derived type, and you will later need to
know that it is of derived type, then it is generally worth the effort
to make sure the compiler knows it, too. If you feel the need for a
cast, then step away from the computer for a little while; try to think
of a way to explain to the compiler, in standard C++, that what you are
doing is safe, so that you do not need the cast.
 
J

Jeff Schwab

Jeff said:
... snip code along these lines:

struct base {
virtual ~base() { }
virtual base* cut() { return new base; }
};

struct derived: base {
virtual derived* cut() { return new derived; }
};

The covariant return type of the cut() method allows the derived class
to override the method, even though it actually returns a different,
more specialized static type. If you were not allowed to return a
covariant type, then derived::cut() would have to return a
pointer-to-base, even when the method was invoked directly on an object
of derived type. In other words, you would have to write code like this:

derived d;
derived* pd = static_cast<derived*>(pd->cut());

derived* pd = static_cast said:
Thanks to the covariant return type, you can write much cleaner code:

derived d;
derived* pd = pd->cut();

derived* pd = d.cut();

Sorry for the typos. Fingers moved faster than brain.
 
J

James Kanze

* Leon -> Alf P. Steinbach:
No, they're in the language for a reason.

And there are several different types of casts, and a moderately
awkward and highly visible syntax for a reason:).
That reason is not to make code that one does not understand,
compile.
stream >> myInt;
Of course it may be that you're talking about binary i/o,
since you mention read().
In that case, consider whether binary i/o really is what you
need. Most often it's not.

And even with binary I/O, casting an int* to a char* in order to
pass it to read is not going to give you anything usable, except
in very limited cases.
 
J

James Kanze

* Thomas J. Gritzan:
class virt_base
{ [...]
virtual virt_base* cut()
{
return new virt_base();
} [...]
};
class virt_derived
: public virt_base
{ [...]
virtual virt_derived* cut()
{
return new virt_derived();
} [...]
};
The covariant return type lets you override a function with
a more strict function, i.e. a function returning a derived
type. However, type checking is done at compile time, so
your compiler has to know, that the object you are calling
cut() on is a virt_derived.
int main()
{
virt_base* my_virt_derived = new virt_derived();
Change this to
virt_derived* my_virt_derived = new virt_derived();
That would make the code compile, but would defeat the pupose
of the code.

What is the purpose of the code? (That's meant to be an honest
question. As far as I can tell, co-variant return types are a
solution looking for a problem---I've yet to find any reasonable
use for them.)
 
M

mr.xiaofan.li

* Thomas J. Gritzan:
(e-mail address removed) wrote:
class virt_base
{
[...]
virtual virt_base* cut()
{
returnnew virt_base();
}
[...]
};
class virt_derived
: public virt_base
{
[...]
virtual virt_derived* cut()
{
returnnew virt_derived();
}
[...]
};
Thecovariantreturntypelets you override a function with
a more strict function, i.e. a function returning a derived
type. However,typechecking is done at compile time, so
your compiler has to know, that the object you are calling
cut() on is a virt_derived.
int main()
{
virt_base* my_virt_derived = new virt_derived();
Change this to
virt_derived* my_virt_derived = new virt_derived();
That would make the code compile, but would defeat the pupose
of the code.

What is the purpose of the code? (That's meant to be an honest
question. As far as I can tell, co-variantreturntypes are a
solution looking for a problem---I've yet to find any reasonable
use for them.)

--
James Kanze (GABI Software) email:[email protected]
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Thanks for you and all replies - The purpose of the code was,
originally, to assign a member v-func that() to each classes which I
would like to get a pointer of THAT particular type when using them. I
did it like

...

virtual derived* that()
{
return this;
}

virtual const derived* that() const
{
return this;
}

and wished it return a derived* pointer which I could dereference to
the full-sized object rather than being sliced to base. I implemented
a generic pool-based container which can copy&store polymorphic
objects and it works well - but I need to know which is which when I
pull them out of the container, thus the idea of using covariant
return types.

If I have to rely on a downcast to get the correct pointer, IMHO I am
not using cov return types at all - I still have to know (at compile
time) which object I am going to cast it. I currently use member flags
to identify this and set up if-else if cascade, which is ugly; I
thought cov return types could resolve this nicely but found out it
didn't work.

Any more thoughts on this?
 
J

James Kanze

* Thomas J. Gritzan:
(e-mail address removed) wrote:
class virt_base
{
[...]
virtual virt_base* cut()
{
returnnew virt_base();
}
[...]
};
class virt_derived
: public virt_base
{
[...]
virtual virt_derived* cut()
{
returnnew virt_derived();
}
[...]
};
Thecovariantreturntypelets you override a function with
a more strict function, i.e. a function returning a derived
type. However,typechecking is done at compile time, so
your compiler has to know, that the object you are calling
cut() on is a virt_derived.
int main()
{
virt_base* my_virt_derived = new virt_derived();
Change this to
virt_derived* my_virt_derived = new virt_derived();
That would make the code compile, but would defeat the pupose
of the code.
What is the purpose of the code? (That's meant to be an honest
question. As far as I can tell, co-variantreturntypes are a
solution looking for a problem---I've yet to find any reasonable
use for them.)
Thanks for you and all replies - The purpose of the code was,
originally, to assign a member v-func that() to each classes which I
would like to get a pointer of THAT particular type when using them. I
did it like

virtual derived* that()
{
return this;
}
virtual const derived* that() const
{
return this;
}
and wished it return a derived* pointer which I could
dereference to the full-sized object rather than being sliced
to base.

Pointers aren't sliced. The pointer still points to an object
with the dynamic type derived, even if it is a base*.

I still don't really understand what you are trying to do.
Something like
Derived* pDerived = pBase->that() ;
doesn't make sense (at least in this context). If you have a
pointer to base, then it isn't necessarily a derived, and if you
know it is in fact a derived, then you can cast the pointer
directly---you don't need co-variant returns. (If you really,
really know, you can use static_cast. Generally speaking,
however, dynamic_cast is better.)

And of course, cases where you want a pointer to the derived
type are not the general rule anyway.
I implemented a generic pool-based container which can
copy&store polymorphic objects and it works well - but I need
to know which is which when I pull them out of the container,
thus the idea of using covariant return types.

But how would covariant return types help you?
If I have to rely on a downcast to get the correct pointer,
IMHO I am not using cov return types at all - I still have to
know (at compile time) which object I am going to cast it.

But you have to know anyway, since that is the target type.
I currently use member flags to identify this and set up
if-else if cascade, which is ugly; I thought cov return types
could resolve this nicely but found out it didn't work.

You can use an if-else of dynamic_cast, if you want. But I'd
wonder if such a polymorphic container is a good solution to
begin with. (There are cases where it is, but it isn't the
usual case.) At any rate, if you want to use the derived
interface, you have to know that the object really is a derived.
You simply cannot do it otherwise, since the object might not be
a derived. And the standard solution for that is dynamic_cast.
 

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,982
Messages
2,570,190
Members
46,736
Latest member
zacharyharris

Latest Threads

Top