Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

covariant return type and forward declaration

75 views
Skip to first unread message

zade

unread,
Nov 11, 2008, 4:12:46 PM11/11/08
to
Codes like below:

struct BaseDescriptor;
struct Base
{
virtual BaseDescriptor* getDescriptor() {return NULL;}
};
struct BaseDescriptor
{
virtual Base* createValue() const {return NULL;}
};

struct DerivedDescriptor;
struct Derived : public Base
{
virtual DerivedDescriptor* getDescriptor() {return NULL;}
};
struct DerivedDescriptor : public BaseDescriptor
{
virtual Derived* createValue() const {return NULL;}
};

MSVS and GCC both give compile error message. In MSVS, it says:

error C2555: 'Covariant::Derived::getDescriptor': overriding virtual
function return type differs and is not covariant from
'Covariant::Base::getDescriptor'

It seems that covariant return type and forward declaration can't be
satsified simutaneously. But how can I correct it?


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Gil

unread,
Nov 12, 2008, 8:13:25 AM11/12/08
to
On Nov 11, 4:12 pm, zade <zhaohongc...@gmail.com> wrote:
> error C2555: 'Covariant::Derived::getDescriptor': overriding virtual
> function return type differs and is not covariant from
> 'Covariant::Base::getDescriptor'
>
> It seems that covariant return type and forward declaration can't be
> satsified simutaneously. But how can I correct it?
>

you can postpone defining Derived class until your DerivedDescriptor
class is completely defined by using a class template to generate
your Derived.

// ----------------------------------
namespace derived_detail {
template< typename T >
struct DerivedGeneric : public T {
virtual DerivedDescriptor* getDescriptor() {
cout << "DerivedGeneric::getDescriptor" << endl;
return NULL;
}
};
} //namespace derived_detail
// ----------------------------------

please see full code below (note the virtual dtor's too).

you could also employ partial specialization on DerivedGeneric.
the detail namespace hides your generic derived (your generator).
users of the hierarchy should simple derive from the typedef'ed
Derive type (that is what behave like your initial Derived
in your example.

cheers,
gil

/*
* test_covariant.cpp
* Created on: Nov 12, 2008
* Author: Gill
*/

#include <iostream>
using std::cout;
using std::endl;

struct BaseDescriptor;

struct Base {
virtual ~Base( ) { }
virtual BaseDescriptor* getDescriptor() {
cout << "Base::getDescriptor" << endl;
return NULL;
}
};

struct BaseDescriptor {
virtual ~BaseDescriptor( ) { }


virtual Base* createValue() const {return NULL;}
};

struct DerivedDescriptor;

namespace derived_detail {
template< typename T >
struct DerivedGeneric : public T {
virtual DerivedDescriptor* getDescriptor() {
cout << "DerivedGeneric::getDescriptor" << endl;
return NULL;
}
};
} //namespace derived_detail

typedef derived_detail::DerivedGeneric< Base > Derived;

struct DerivedDescriptor : public BaseDescriptor {
virtual Derived * createValue() const {return NULL;}
};

int main( ) {
Base * pb = new Derived;
pb->getDescriptor( );
delete pb;
return EXIT_SUCCESS;
}

// toolchain: MinGW GCC

Yechezkel Mett

unread,
Nov 12, 2008, 12:24:07 PM11/12/08
to
On Nov 11, 11:12 pm, zade <zhaohongc...@gmail.com> wrote:
> Codes like below:
>
> struct BaseDescriptor;
> struct Base
> {
> virtual BaseDescriptor* getDescriptor() {return NULL;}
> };
> struct BaseDescriptor
> {
> virtual Base* createValue() const {return NULL;}
> };
>
> struct DerivedDescriptor;
> struct Derived : public Base
> {
> virtual DerivedDescriptor* getDescriptor() {return NULL;}
> };
> struct DerivedDescriptor : public BaseDescriptor
> {
> virtual Derived* createValue() const {return NULL;}
> };
>
> MSVS and GCC both give compile error message. In MSVS, it says:
>
> error C2555: 'Covariant::Derived::getDescriptor': overriding virtual
> function return type differs and is not covariant from
> 'Covariant::Base::getDescriptor'
>
> It seems that covariant return type and forward declaration can't be
> satsified simutaneously.

Yes. This is because in the line

virtual DerivedDescriptor* getDescriptor() {return NULL;}

the compiler does not yet know that DerivedDescriptor derives from
BaseDescriptor, because it is incomplete ('forward declared').

> But how can I correct it?

You can't directly, but you can work around the problem with the Non-
Virtual Interface idiom like so:

struct BaseDescriptor;
struct Base
{
BaseDescriptor* getDescriptor() {return
getDescriptorImpl();}
private:
virtual BaseDescriptor* getDescriptorImpl() {return


NULL;}
};
struct BaseDescriptor
{
virtual Base* createValue() const {return NULL;}
};

struct DerivedDescriptor;
struct Derived : public Base
{

DerivedDescriptor* getDescriptor();
{ return static_cast<DerivedDescriptor*>
(getDescriptorImpl()); }
private:
virtual BaseDescriptor* getDescriptorImpl() {return


NULL;}
};
struct DerivedDescriptor : public BaseDescriptor
{
virtual Derived* createValue() const {return NULL;}
};

Effectively you implement by hand what the compiler would do for you.
You must be careful to ensure that Derived::getDescriptorImpl() (and
all overrides) really do return a DerivedDescriptor* (or null, as
here).

Yechezkel Mett

Seungbeom Kim

unread,
Nov 12, 2008, 12:25:37 PM11/12/08
to
zade wrote:
> Codes like below:
>
> struct BaseDescriptor;
> struct Base
> {
> virtual BaseDescriptor* getDescriptor() {return NULL;}
> };
> struct BaseDescriptor
> {
> virtual Base* createValue() const {return NULL;}
> };
>
> struct DerivedDescriptor;
> struct Derived : public Base
> {
> virtual DerivedDescriptor* getDescriptor() {return NULL;}
> };
> struct DerivedDescriptor : public BaseDescriptor
> {
> virtual Derived* createValue() const {return NULL;}
> };
>
> MSVS and GCC both give compile error message. In MSVS, it says:
>
> error C2555: 'Covariant::Derived::getDescriptor': overriding virtual
> function return type differs and is not covariant from
> 'Covariant::Base::getDescriptor'
>
> It seems that covariant return type and forward declaration can't be
> satsified simutaneously. But how can I correct it?

Maybe it would be nice if we could specify the base classes
in forward declarations, too.

struct DerivedDescriptor : public Base; // not complete yet
struct Derived : public Base
{
virtual DerivedDescriptor* getDescriptor() // covariant!
};

--
Seungbeom Kim

Alberto Ganesh Barbati

unread,
Nov 12, 2008, 12:24:03 PM11/12/08
to
zade ha scritto:

> Codes like below:
>
> struct BaseDescriptor;
> struct Base
> {
> virtual BaseDescriptor* getDescriptor() {return NULL;}
> };
> struct BaseDescriptor
> {
> virtual Base* createValue() const {return NULL;}
> };
>
> struct DerivedDescriptor;
> struct Derived : public Base
> {
> virtual DerivedDescriptor* getDescriptor() {return NULL;}
> };
> struct DerivedDescriptor : public BaseDescriptor
> {
> virtual Derived* createValue() const {return NULL;}
> };
>
> MSVS and GCC both give compile error message. In MSVS, it says:
>
> error C2555: 'Covariant::Derived::getDescriptor': overriding virtual
> function return type differs and is not covariant from
> 'Covariant::Base::getDescriptor'
>
> It seems that covariant return type and forward declaration can't be
> satsified simutaneously. But how can I correct it?
>
>

The fact is that to use a type as a covariant return type, the type must
be complete. A forward declaration alone, which introduces an incomplete
type, doesn't suffice.

In your case, I can see no solution: both Derived and DerivedDescriptor
requires the other to be complete and you can't get that in any way. You
have to surrender one of the two classes.

HTH,

Ganesh

Hisense

unread,
Nov 13, 2008, 6:25:02 PM11/13/08
to
> [ Seehttp://www.gotw.ca/resources/clcm.htmfor info about ]
> [ comp.lang.c++.moderated. First time posters: Do this! ]- 隐藏被引用文字 -
>
> - 显示引用的文字 -

It works though, but I dont think it is a better design.

The struct Derived has a public member,
"DerivedDescriptor* getDescriptor()"
who hides the one from struct Base
"BaseDescriptor* getDescriptor()"

but it is _non-virtual_, and no dynamic features.

olekk

unread,
Nov 13, 2008, 6:30:22 PM11/13/08
to

I suppose that you can also parametrize Descriptor with descripted
object type

template<typename Descripted>
struct DerivedDescriptor : public BaseDescriptor
{
virtual Descripted* createValue() const {return 0;}
};

struct Derived : public Base
{
virtual DerivedDescriptor<Derived>* getDescriptor()
{ return 0;}
};

Regards

Faisal Vali

unread,
Nov 13, 2008, 6:34:32 PM11/13/08
to
On Nov 11, 3:12 pm, zade <zhaohongc...@gmail.com> wrote:

<snip code regarding circular covariant return type requirements>


> It seems that covariant return type and forward declaration can't be
> satsified simutaneously. But how can I correct it?
>

Well if you don't mind using templates, you can try something like
this:

template <class T>
struct B {
virtual T* getDescriptor();
};

template<class T>
struct D : B<typename T::Base>
{
virtual T* getDescriptor();
};

struct BDescriptor {
virtual B<BDescriptor>* createValue();
};
struct DDescriptor : BDescriptor {
typedef BDescriptor Base;
virtual D<DDescriptor>* createValue();
};

regards,
Faisal Vali
Radiation Oncology
Loyola

red floyd

unread,
Nov 14, 2008, 2:28:06 AM11/14/08
to
On Nov 12, 9:25 am, Seungbeom Kim <musip...@bawi.org> wrote:
if we could specify the base classes
> in forward declarations, too.
>
> struct DerivedDescriptor : public Base; // not complete yet

I would love to see this as an added feature. Of course, it's
probably too late for C++0x.


--

Vidar Hasfjord

unread,
Nov 14, 2008, 2:44:13 AM11/14/08
to
On Nov 12, 5:25 pm, Seungbeom Kim <musip...@bawi.org> wrote:
> zade wrote:
> [...]

> > It seems that covariant return type and forward declaration can't be
> > satsified simutaneously. But how can I correct it?
>
> Maybe it would be nice if we could specify the base classes
> in forward declarations, too.
>
> struct DerivedDescriptor : public Base; // not complete yet
> struct Derived : public Base
> {
> virtual DerivedDescriptor* getDescriptor() // covariant!
> };

I suggested that in an earlier thread with a similar, but less
convincing, rationale. The OP's case has provided another (better)
argument for such a feature.

http://groups.google.com/group/comp.lang.c++.moderated/browse_frm/thread/9f05934ee81e120b


--

Anders Dalvander

unread,
Nov 14, 2008, 5:22:44 PM11/14/08
to
> Maybe it would be nice if we could specify the base classes
> in forward declarations, too.
>
> struct DerivedDescriptor : public Base; // not complete yet

It should be allowed as C++0x will allow the following forward
declaration for enumerations:

enum class MyEnum : unsigned int;

Cheers,
Anders Dalvander


--

Yechezkel Mett

unread,
Nov 16, 2008, 12:53:57 PM11/16/08
to
> > [ Seehttp://www.gotw.ca/resources/clcm.htmforinfo about ]

> > [ comp.lang.c++.moderated. First time posters: Do this! ]- 隐藏被引用文字 -
>
> > - 显示引用的文字 -
>
> It works though, but I dont think it is a better design.
>
> The struct Derived has a public member,
> "DerivedDescriptor* getDescriptor()"
> who hides the one from struct Base
> "BaseDescriptor* getDescriptor()"

So what? Both functions do the same thing.

>
> but it is _non-virtual_, and no dynamic features.
>

I don't know what you mean by "no dynamic features". It gains the
effect of a virtual function by dispatching to the virtual
implementation.

Yechezkel Mett

0 new messages