boost::shared_ptr and conversion to base class

R

Ryan Mitchley

Hi all

Given

bool bResult;
shared_ptr<cSampleData> pNewData;
shared_ptr<cBase> pNewBase;

where cSampleData is descended from cBase, the following gives me a valid
pNewData to the correct type:

bResult = Input(INPORT_snapshot)->ReceiveNow(pNewBase);
pNewData = pNewBase;

This works fine, although the assignment conversion seems to be a bit
longwinded.

ReceiveNow is defined as:
bool ReceiveNow(shared_ptr<CBase> & pNewObject);

However, the compiler won't allow me to call the following, due to the
inability to initialize the reference parameter of ReceiveNow:
bResult = Input(INPORT_snapshot)->ReceiveNow(pNewData);

Adding a cast compiles correctly:
bResult =
Input(INPORT_snapshot)->ReceiveNow(boost::dynamic_pointer_cast<CBase>(pNewDa
ta));

but the pNewData is null afterwards.

Is there any way of passing the pointer to the derived type (pNewData) to
the function expecting a base class pointer in a single function call?

Is the shared_ptr getting confused by the dynamic_pointer_cast and deleting
the object incorrectly? Do I need to use a weak_ptr?

Any suggestions?

Thanks!

Ryan
 
M

Maxim Yegorushkin

Ryan Mitchley said:
bool bResult;
shared_ptr<cSampleData> pNewData;
shared_ptr<cBase> pNewBase;

where cSampleData is descended from cBase, the following gives me a valid
pNewData to the correct type:

bResult = Input(INPORT_snapshot)->ReceiveNow(pNewBase);
pNewData = pNewBase;

The last line must not compile as you are trying to assign a pointer to base to a pointer to derived what essentially is a downcast. There is no implicit downcast in C++ said:
This works fine, although the assignment conversion seems to be a bit
longwinded.

ReceiveNow is defined as:
bool ReceiveNow(shared_ptr<CBase> & pNewObject);

I assume CBase here and cBase above refer the same type.
However, the compiler won't allow me to call the following, due to the
inability to initialize the reference parameter of ReceiveNow:
bResult = Input(INPORT_snapshot)->ReceiveNow(pNewData);

Adding a cast compiles correctly:
bResult =
Input(INPORT_snapshot)->ReceiveNow(boost::dynamic_pointer_cast<CBase>(pNewDa
ta));

but the pNewData is null afterwards.

Neither ReceiveNow(pNewBase) nor ReceiveNow(dynamic_pointer_cast<CBase>(pNewBase)) should compile. The reason is that ReceiveNow() function takes a non-const reference. In both cases you are tring to feed the function with an r-value. Non-const reference can not be bound to r-value (microsoft compilers allow to do that).

1) ReceiveNow(pNewBase) - although cBase and cSampleData types have an inheritance relationship, shared_ptr<cSampleData> and shared_ptr<cBase> are distinct unrelated types. There exists an implicit user defined conversion from shared_ptr<U> to shared_ptr<T> when U is convertible to T. That conversion yields an r-value that can not be bound to non-const reference.

2) boost::dynamic_pointer_cast said:
Is there any way of passing the pointer to the derived type (pNewData) to
the function expecting a base class pointer in a single function call?

Make the function take a const reference, like ReceiveNow(shared_ptr<CBase> const& pNewObject). Or better make it take a reference to CBase, like ReceiveNow(CBase& pNewObject) and dereference a pointer before a call, like ReceiveNow(*pNewBase). In the latter case you avoid some obscurity and overhead related to shared_ptr<> conversions and the function becomes more generic as it does not depend on whatever (smart) pointers you use.
 
R

Ryan Mitchley

Hi Maxim

You wrote on Fri, 25 Jun 2004 11:43:05 +0400:

??>> bool bResult;
??>> shared_ptr<cSampleData> pNewData;
??>> shared_ptr<cBase> pNewBase;
??>>
??>> where cSampleData is descended from cBase, the following gives me a
??>> valid pNewData to the correct type: bResult =
??>> Input(INPORT_snapshot)->ReceiveNow(pNewBase); pNewData = pNewBase;

MY> The last line must not compile as you are trying to assign a pointer to
MY> base to a pointer to derived what essentially is a downcast. There is
MY> no implicit downcast in C++, so you must use appropriate form of
MY> xxx_cast<>.

Erm, it *does* compile fine using the Intel C++ Compiler 8.0. Maybe the
Boost library is doing the conversion...?

??>> This works fine, although the assignment conversion seems to be a bit
??>> longwinded.
??>>
??>> ReceiveNow is defined as:
??>> bool ReceiveNow(shared_ptr<CBase> & pNewObject);

MY> I assume CBase here and cBase above refer the same type.

Yes, sorry - typo.

??>> However, the compiler won't allow me to call the following, due to the
??>> inability to initialize the reference parameter of ReceiveNow:
??>> bResult = Input(INPORT_snapshot)->ReceiveNow(pNewData);
??>>
??>> Adding a cast compiles correctly:
??>> bResult =
??>> Input(INPORT_snapshot)->ReceiveNow(boost::dynamic_pointer_cast<CBase>(
??>> pNewDa ta)); but the pNewData is null afterwards.

MY> Neither ReceiveNow(pNewBase) nor
MY> ReceiveNow(dynamic_pointer_cast<CBase>(pNewBase)) should compile.

The first line doesn't compile, while the second does, without warnings.
Unfortunately, pNewBase is null after the call (even though ReceiveNow is
definitely returning a valid object).

MY> Make the function take a const reference, like
MY> ReceiveNow(shared_ptr<CBase> const& pNewObject).

Okay, I don't think this could work, as the ReceiveNow function needs to
update pNewObject with the object that has just been received... It's not
const - that's why it really needs to be pass by (non-const) reference.

MY> Or better make it take a reference to CBase, like ReceiveNow(CBase&
MY> pNewObject) and dereference a pointer before a call, like
MY> ReceiveNow(*pNewBase). In the latter case you avoid some obscurity and
MY> overhead related to shared_ptr<> conversions and the function becomes
MY> more generic as it does not depend on whatever (smart) pointers you
MY> use.

Unfortunately, the ReceiveNow function needs to be able to return a null
result (in pNewObject), since it is possible for nothing to be received.

Thanks for your time.

Ryan
 
M

Maxim Yegorushkin

Ryan Mitchley said:
??>> bool bResult;
??>> shared_ptr<cSampleData> pNewData;
??>> shared_ptr<cBase> pNewBase;
??>>
??>> where cSampleData is descended from cBase, the following gives me a
??>> valid pNewData to the correct type: bResult =
??>> Input(INPORT_snapshot)->ReceiveNow(pNewBase); pNewData = pNewBase;

MY> The last line must not compile as you are trying to assign a pointer to
MY> base to a pointer to derived what essentially is a downcast. There is
MY> no implicit downcast in C++, so you must use appropriate form of
MY> xxx_cast<>.

Erm, it *does* compile fine using the Intel C++ Compiler 8.0. Maybe the
Boost library is doing the conversion...?

Well, now I seem to remember that there was a discussion on this subject in boost newsgroup. But I could not find it, shame on me...

I tried the code on gcc 2.96 - it does not compile with correct diagnostic:

boost::shared_ptr<T>& boost::shared_ptr<T>::eek:perator= (const boost::shared_ptr<Y> &) [with Y = base, T = derived]':
experiment.cpp:14: instantiated from here
/home/my/lib/boost/boost/shared_ptr.hpp:212: cannot convert `base *const' to `derived *' in assignment
??>> However, the compiler won't allow me to call the following, due to the
??>> inability to initialize the reference parameter of ReceiveNow:
??>> bResult = Input(INPORT_snapshot)->ReceiveNow(pNewData);
??>>
??>> Adding a cast compiles correctly:
??>> bResult =
??>> Input(INPORT_snapshot)->ReceiveNow(boost::dynamic_pointer_cast<CBase>(
??>> pNewDa ta)); but the pNewData is null afterwards.

MY> Neither ReceiveNow(pNewBase) nor
MY> ReceiveNow(dynamic_pointer_cast<CBase>(pNewBase)) should compile.

The first line doesn't compile, while the second does, without warnings.
Unfortunately, pNewBase is null after the call (even though ReceiveNow is
definitely returning a valid object).

That is because dynamic_pointer_cast said:
MY> Make the function take a const reference, like
MY> ReceiveNow(shared_ptr<CBase> const& pNewObject).

Okay, I don't think this could work, as the ReceiveNow function needs to
update pNewObject with the object that has just been received... It's not
const - that's why it really needs to be pass by (non-const) reference.
[]

Unfortunately, the ReceiveNow function needs to be able to return a null
result (in pNewObject), since it is possible for nothing to be received.

Why can't you make the function to return a pointer to a new object or zero pointer if there is none like:

shared_ptr<CBase> ReceiveNow(shared_ptr<CBase> const&);

and then call it like:

if(shared_ptr<CBase> new_object = ReceiveNow(pNewBase))
{
// new_object refers the new object here
}
else
{
// no new object received
}
 
R

Ryan Mitchley

Hi Maxim

Thanks for you reply, once again!

You wrote on Fri, 25 Jun 2004 17:06:40 +0400:

??>> Erm, it *does* compile fine using the Intel C++ Compiler 8.0. Maybe
??>> the Boost library is doing the conversion...?

MY> Well, now I seem to remember that there was a discussion on this
MY> subject in boost newsgroup. But I could not find it, shame on me...

MY> I tried the code on gcc 2.96 - it does not compile with correct
MY> diagnostic:

That's strange - I was kind of under the impression that the Intel C++
Compiler was a relatively strict compiler . . . I suppose that the
requirement for compatibility with MS Visual C++ necessitates some
relaxation of the strictness! Possibly there are some compiler flags that
will affect this behaviour . . .

MY> Why can't you make the function to return a pointer to a new object or
MY> zero pointer if there is none like:

MY> shared_ptr<CBase> ReceiveNow(shared_ptr<CBase> const&);

MY> and then call it like:

Okay - this is pretty much what I've done, successfully, except that it ends
up being even simpler:
shared_ptr<CBase> ReceiveNow();

I can now successfully call
shared_ptr<cSampleData> pNewData = Input(INPORT_snapshot)->ReceiveNow();

I'm curious, though, why this pointer assignment of two different types
works in the return value, but the pass by reference (function parameter)
didn't work . . .

Ryan
 
T

tom_usenet

Hi all

Given

bool bResult;
shared_ptr<cSampleData> pNewData;
shared_ptr<cBase> pNewBase;

where cSampleData is descended from cBase, the following gives me a valid
pNewData to the correct type:

bResult = Input(INPORT_snapshot)->ReceiveNow(pNewBase);
pNewData = pNewBase;

The second line should require a dynamic_pointer_cast.
This works fine, although the assignment conversion seems to be a bit
longwinded.

ReceiveNow is defined as:
bool ReceiveNow(shared_ptr<CBase> & pNewObject);

However, the compiler won't allow me to call the following, due to the
inability to initialize the reference parameter of ReceiveNow:
bResult = Input(INPORT_snapshot)->ReceiveNow(pNewData);

Yes, of course not, since ReceiveNow could assign a cAnotherClass to
the pointer, and then you'd have a cSampleData pointer pointing to an
unrelated class. Similarly:

bool ReceiveNow(CBase*& oNewObject);
cSampleData* ptr;
ReceiveNow(ptr);

also won't work, for the same reason (technically, you can't bind a
non-const reference to an rvalue).
Adding a cast compiles correctly:
bResult =
Input(INPORT_snapshot)->ReceiveNow(boost::dynamic_pointer_cast<CBase>(pNewDa
ta));

but the pNewData is null afterwards.

I'm very surprised the above makes any difference. I would assume that
the cast would return an rvalue, so if it compiles, your compiler is
broken.
Is there any way of passing the pointer to the derived type (pNewData) to
the function expecting a base class pointer in a single function call?

No, since such a think breaks the contract of your ReceiveNow
function.
Is the shared_ptr getting confused by the dynamic_pointer_cast and deleting
the object incorrectly? Do I need to use a weak_ptr?

Any suggestions?

The two step process is the correct way to do it. Or if ReceiveNow
really returns some cSampleData, you should update the interface to
take a shared_ptr<cSampleData>&.

Tom
 
M

Maxim Yegorushkin

[]
shared_ptr<CBase> ReceiveNow();

I can now successfully call
shared_ptr<cSampleData> pNewData = Input(INPORT_snapshot)->ReceiveNow();

I'm curious, though, why this pointer assignment of two different types
works in the return value, but the pass by reference (function parameter)
didn't work . . .

Intel C++ compiler by default lets one assign a pointer to base to a
pointer to derived.

struct B {};
struct D : B {};

int main()
{
D* d;
B* b;
d = b;
}

produces only:

warning #556: a value of type "B *" cannot be assigned to an entity of
type "D *"
d = b;
^

It is possible to make this warning an error.
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top