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! ]
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
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
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
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
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.
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
<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
I would love to see this as an added feature. Of course, it's
probably too late for C++0x.
--
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
--
It should be allowed as C++0x will allow the following forward
declaration for enumerations:
enum class MyEnum : unsigned int;
Cheers,
Anders Dalvander
--
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