Casting class1<class2> to class1<class3 >

A

Andy Lomax

I'm using a library where the supplier has provided base class 'foo',
and a reference counting class which wraps 'foo':

------------------- library code -----------------------
template<class T>
refcount {
... // reference counting wrapper; like shared_ptr
};

class foo {
...
};

typedef refcount<foo> fooref;
--------------------------------------------------------

Now, when I use the library, I have to derive from 'foo' to make it
useful. I also have to add a new function which is *not* declared
virtual in 'foo':

----------------------- my code ------------------------
class myfoo : public foo {
...
void myfunc(void);
}
--------------------------------------------------------

Problem: if I know that a 'fooref' actually points to a 'myfoo', how
do I call 'myfunc'?

void a(fooref b) {
b->myfunc(); // compiler error: foo has no myfunc
static_cast<myfooref>(b)->myfunc(); // 1
static_cast<myfoo *>(&*b)->myfunc(); // 2
}

Both (1) and (2) compile, but are they the same thing? I understand
(2), but (1) is more confusing. Can I cast a templated class in this
way?

Cheers

AL
 
D

Donovan Rebbechi

I'm using a library where the supplier has provided base class 'foo',
and a reference counting class which wraps 'foo':

------------------- library code -----------------------
template<class T>
refcount {
... // reference counting wrapper; like shared_ptr
};

class foo {
...
};

typedef refcount<foo> fooref;
--------------------------------------------------------

Now, when I use the library, I have to derive from 'foo' to make it
useful. I also have to add a new function which is *not* declared
virtual in 'foo':

----------------------- my code ------------------------
class myfoo : public foo {
...
void myfunc(void);
}
--------------------------------------------------------

Problem: if I know that a 'fooref' actually points to a 'myfoo', how
do I call 'myfunc'?

void a(fooref b) {
b->myfunc(); // compiler error: foo has no myfunc
static_cast<myfooref>(b)->myfunc(); // 1
static_cast<myfoo *>(&*b)->myfunc(); // 2
}

Both (1) and (2) compile, but are they the same thing? I understand
(2), but (1) is more confusing. Can I cast a templated class in this
way?

You can't generally cast anything to anything, no (and the different
parametrizations *are* allowed to be as unrelated as you like -- for example,
you could specialise one of them to inherit from vector<int>, and another to
inherit list<void*> if you wanted to). Depending on what conversion operators
refcount supports, it might work.

Now some thoughts --
(1) If you're casting down a class heirarchy, it would be safer to use a
dynamic cast. Then you get a runtime check to make sure you are really right
(and that it does really point to what you think it does)

(2) But it's better to avoid this. What is stopping you from making void a()
take a parameter of type myfoo instead ? If you've got several derived classes
of foo, you could factor them into a common base class. There are legitimate
uses for downtime casting, but the abuses are more common than the uses.

Cheers,
 
A

Andy Lomax

You can't generally cast anything to anything, no (and the different
parametrizations *are* allowed to be as unrelated as you like -- for example,
you could specialise one of them to inherit from vector<int>, and another to
inherit list<void*> if you wanted to). Depending on what conversion operators
refcount supports, it might work.

Ah.. forgotten about conversion operators. I've checked refcount, and
the only conversion op it has is:

template<class T>
class refcount {
operator T* () const { return static_cast<T*>(ref->ptr); }
};

So the code compiles anyway even without a conversion operator.
Now some thoughts --
(1) If you're casting down a class heirarchy, it would be safer to use a
dynamic cast. Then you get a runtime check to make sure you are really right
(and that it does really point to what you think it does)

Thanks. Presumably the dynamic_cast overhead is higher, though, so I
should stick with static_cast if I can guarantee no problems?
(2) But it's better to avoid this. What is stopping you from making void a()
take a parameter of type myfoo instead ? If you've got several derived classes
of foo, you could factor them into a common base class. There are legitimate
uses for downtime casting, but the abuses are more common than the uses.

Two reasons:
(a) the base 'foo' provides a lot of routines that I need, and they
all return a 'fooref', so I can't assign the result to a
refcount<myfoo> without downcasting anyway (ie. the problem just
moves):

typedef refcount<myfoo> myfooref;

void a(myfooref b) {
b->myfunc(); // Ok; solves immediate problem, but...
myfooref c = b->x(); // doesn't compile: x returns a fooref
myfooref d = static_cast<myfooref>(b->x()); // Ok
}

(b) This is a bit more nebulous. I'm not sure why, but it seems to me
that my own code should be written to use the original base class as
much as possible, rather than forcing use of my own subclass.
If you've got several derived classes
of foo, you could factor them into a common base class.

As it happens, the library provides 'foo'; I derive 'myfoo' from it; I
then derive another 60-odd classes from 'myfoo'.

Cheers

AL
 
D

Donovan Rebbechi

Two reasons:
(a) the base 'foo' provides a lot of routines that I need, and they
all return a 'fooref', so I can't assign the result to a
refcount<myfoo> without downcasting anyway (ie. the problem just
moves):

typedef refcount<myfoo> myfooref;

void a(myfooref b) {
b->myfunc(); // Ok; solves immediate problem, but...
myfooref c = b->x(); // doesn't compile: x returns a fooref
myfooref d = static_cast<myfooref>(b->x()); // Ok
}

My first thought was covariant return types, but of course that's not
going to work because the return value isn't a pointer, it's a smart
pointer.
(b) This is a bit more nebulous. I'm not sure why, but it seems to me
that my own code should be written to use the original base class as
much as possible, rather than forcing use of my own subclass.


As it happens, the library provides 'foo'; I derive 'myfoo' from it; I
then derive another 60-odd classes from 'myfoo'.

Sounds like a good way to do it.

I wonder if there's a way you could contain the problem ? For example, by
writing a class whose responsibility is to take care of the downcasting.
It's a similar idea to auto_ptr or even scoped_ptr -- the point of the
class is to encapsulate the potentially dangerous operation (in this case,
downcasting). You'd implement it by giving it the same interface as myfoo,
except functions that return fooref would be adapted to return myfooref.
You could do this via private inheritance for example, and just delegate
calls to the contained myfooref object.

Cheers,
 

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,781
Messages
2,569,615
Members
45,296
Latest member
HeikeHolli

Latest Threads

Top