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

problems with polymorphism and inheritance with templates

1 view
Skip to first unread message

Frank Buss

unread,
Aug 29, 2010, 11:47:28 AM8/29/10
to
This code:


template<class T>
class Foo
{
public:
T* member;
};

class Base
{
};

typedef Foo<Base> BaseFoo;

class Derived
{
};

typedef Foo<Derived> DerivedFoo;

int main(int argc, char** argv)
{
DerivedFoo* dfoo = new DerivedFoo();
BaseFoo* bfoo = dfoo;
return 0;
}


doesn't compile, the compiler says "cannot convert DerivedFoo* to
BaseFoo* in initialization". How can I fix it? Of course, this one works:

Derived* derived = new Derived();
Base* base = derived;

I know that something similiar (but not really the same) is possible,
because shared_ptr can do it, e.g. this one compiles with g++ 4.3.2:


#include <tr1/memory>

using namespace std::tr1;

class Base
{
};

typedef shared_ptr<Base> BasePtr;

class Derived : public Base
{
};

typedef shared_ptr<Derived> DerivedPtr;

int main(int argc, char** argv)
{
DerivedPtr dptr(new Derived());
BasePtr bptr = dptr;
return 0;
}


--
Frank Buss, http://www.frank-buss.de
piano and more: http://www.youtube.com/user/frankbuss


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

Daniel Krügler

unread,
Aug 30, 2010, 10:35:34 AM8/30/10
to
On 29 Aug., 17:47, Frank Buss <f...@frank-buss.de> wrote:
> This code:
>
> template<class T>
> class Foo
> {
> public:
> T* member;
> };
>
> class Base
> {
> };
>
> typedef Foo<Base> BaseFoo;
>
> class Derived
> {
> };

In consideration of what you wrote later combined with your
chosen naming conventions, I assume this was actually
meant to be:

class Derived : public Base
{
};

> typedef Foo<Derived> DerivedFoo;
>
> int main(int argc, char** argv)
> {
> DerivedFoo* dfoo = new DerivedFoo();
> BaseFoo* bfoo = dfoo;
> return 0;
> }
>
> doesn't compile, the compiler says "cannot convert ‘DerivedFoo*’ to
> ‘BaseFoo*’ in initialization". How can I fix it? Of course, this one works:
>
> Derived* derived = new Derived();
> Base* base = derived;
>
> I know that something similiar (but not really the same) is possible,
> because shared_ptr can do it, e.g. this one compiles with g++ 4.3.2:
>
> #include <tr1/memory>
>
> using namespace std::tr1;
>
> class Base
> {
> };
>
> typedef shared_ptr<Base> BasePtr;
>
> class Derived : public Base
> {
> };
>
> typedef shared_ptr<Derived> DerivedPtr;
>
> int main(int argc, char** argv)
> {
> DerivedPtr dptr(new Derived());
> BasePtr bptr = dptr;
> return 0;
> }

It is quite easy to simulate a behaviour that follows
the strategy of shared_ptr, unique_ptr, and similar
classes that can reasonably accept specializations
for different types than the own one. But it is impossible
to "simulate" a base-derived relationship without
introducing such a relation.

The first question is: Why do you need the template,
if you want a specific class hierarchy? Either Foo is
supposed to be some form of a smart pointer, then
there is no reason to create objects in the free store
and to require base-derived relations among smart
pointers. Or Foo is something else, but the question
is: What does it model?

HTH & Greetings from Bremen,

Daniel Krügler


--

SG

unread,
Aug 30, 2010, 7:59:49 PM8/30/10
to
On 29 Aug., 17:47, Frank Buss wrote:
> This code:
>
> template<class T>
> class Foo
> {
> public:
> T* member;
> };

Ok, let's assume Foo<T> has a default constructor that initializes
'member' with a null pointer.

> class Base
> {
> };
>
> typedef Foo<Base> BaseFoo;
>
> class Derived
> {
> };

It seems, you forgot to add Base as a base class for Derived here.

> typedef Foo<Derived> DerivedFoo;
>
> int main(int argc, char** argv)
> {
> DerivedFoo* dfoo = new DerivedFoo();
> BaseFoo* bfoo = dfoo;
> return 0;
> }

Why do you 'new' DerivedFoo here?

> doesn't compile, the compiler says "cannot convert ‘DerivedFoo*’ to
> ‘BaseFoo*’ in initialization".

DerivedFoo and BaseFoo are two distinct types that have no inheritence
relationship. But you can make a DerivedFoo convertible to a BaseFoo
and this is probably what you really wanted to do:

int main(int argc, char** argv)
{

DerivedFoo dfoo;
BaseFoo bfoo = dfoo;
return 0;
}

You can do this by providing a templated conversion constructor in the
Foo class template:

template<class T>
class Foo
{
public:
T* member;

Foo() : member(0) {} // default ctor

template<class U>
Foo(Foo<U> const& f) : member(f.member) {}
};

As a bonus you might want to disable this templated conversion
constructor in case U* is not convertible to T* for overloading
reasons. It's possible with a bit of meta-programming but I think this
would be too much information for now.

> I know that something similiar (but not really the same) is possible,
> because shared_ptr can do it, e.g. this one compiles with g++ 4.3.2:

> [...]
> typedef shared_ptr<Base> BasePtr;


> typedef shared_ptr<Derived> DerivedPtr;
>
> int main(int argc, char** argv)
> {
> DerivedPtr dptr(new Derived());
> BasePtr bptr = dptr;
> return 0;
> }

Right. The thing is DerivedPtr is convertible to BasePtr but the types
are not reference-related because there is no inheritence relationship
between these two types. And since they are not reference-related you
cannot use a pointer of type BasePtr* to point to an object of type
DerivedPtr.

Maybe the typedefs are confusing you. You are aware of the fact that
BasePtr* is a pointer to a struct containing a pointer to Base, right?
The inheritence relationship is only between Base and Derived, not
between BasePtr and DerivedPtr. Is it possible that you missed one
level of indirection?

Cheers!
SG


--

Frank Buss

unread,
Sep 13, 2010, 6:25:48 PM9/13/10
to
SG wrote:

>> class Derived
>> {
>> };
>
> It seems, you forgot to add Base as a base class for Derived here.

Yes, this was a copy and paste error.

>
>> typedef Foo<Derived> DerivedFoo;
>>
>> int main(int argc, char** argv)
>> {
>> DerivedFoo* dfoo = new DerivedFoo();
>> BaseFoo* bfoo = dfoo;
>> return 0;
>> }
>
> Why do you 'new' DerivedFoo here?

The real problem was a bit more complicated: Sometimes I have member
variables and methods like this:

DerivedFoo m_dfoo;
DerivedFoo* getDerivedFoo() { return &m_dfoo; }

and sometimes like this:

BaseFoo m_bfoo;
BaseFoo* getBaseFoo() { return &m_bfoo; }

And finally I have methods, which accepts BaseFoo*, only. I can't use
Base*, because I need functionality from the Foo template, e.g. providing
meta-information, like the member variable name of a Foo object in the
containing class.

> DerivedFoo and BaseFoo are two distinct types that have no inheritence
> relationship. But you can make a DerivedFoo convertible to a BaseFoo
> and this is probably what you really wanted to do:
>
> int main(int argc, char** argv)
> {
> DerivedFoo dfoo;
> BaseFoo bfoo = dfoo;
> return 0;
> }
>
> You can do this by providing a templated conversion constructor in the
> Foo class template:
>
> template<class T>
> class Foo
> {
> public:
> T* member;
> Foo() : member(0) {} // default ctor
>
> template<class U>
> Foo(Foo<U> const& f) : member(f.member) {}
> };

Thanks, that's interesting, but doesn't help for my case. Now I have moved
all members from Derived to Base, with some virtual methods. There are not
many, the main difference is the constructor of "Derived", and this works.
Maybe I have to refactor the architecture later a bit, when I have some
more time, e.g. not using templates, but moving the functionality of the
Foo template to some base class of Base.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

0 new messages