|  | Specs for shared_ptr; compatibility between classic pointers |  | |
| | | Fokko Beekhof |  |
| Posted: Sat Aug 30, 2008 3:28 pm Post subject: Specs for shared_ptr; compatibility between classic pointers |  |
| |  | |
Hello,
recently, I posted a question about a difference in behaviour between regular pointers and shared_ptrs on comp.lang.c++ . Maybe the outcome of the conversation is relevant to someone here, so posting a summary here.
The following code works: --------------------------------------------- struct BaseA { int x; };
struct BaseB { double x; };
struct DerivA : public BaseA { int y; };
struct DerivB : public BaseB { double y; };
struct S { S(BaseA * pa_) : pa(pa_), pb(0) {} S(BaseB * pb_) : pa(0), pb(pb_) {}
BaseA * pa; BaseB * pb; };
int main() { S s(new DerivA()); // works fine delete s.pa;
return 0; } ---------------------------------------------
When we replace classical pointers with shared_ptrs, it does not work anymore:
-------------------------------------------------- #include <tr1/memory>
struct BaseA { int x; };
struct BaseB { double x; };
struct DerivA : public BaseA { int y; };
struct DerivB : public BaseB { double y; };
struct S { S(std::tr1::shared_ptr<BaseA> pa_) : pa(pa_) {} S(std::tr1::shared_ptr<BaseB> pb_) : pb(pb_) {}
std::tr1::shared_ptr<BaseA> pa; std::tr1::shared_ptr<BaseB> pb; };
int main() { // S s(std::tr1::shared_ptr<BaseA>(new DerivA()) ); // works // S s( new DerivA() ); // Doesn't work, SP constructor is explicit S s(std::tr1::shared_ptr<DerivA>(new DerivA()) ); // breaks
return 0; } --------------------------------------------------
Essentially, it breaks because the polymorphism works in function arguments, but not in template arguments. Shared_ptrs here function according to the specs of tr1, and the code does not compile.
Kai-Uwe Bux commented the following: --------------- Begin Quote ----------------
| Quote: | Could anyone comment on this ? Should option #3 with shared_ptr<DerivA work or not ?
|
According to the technical report TR1, it should not work.
| Quote: | Conceptually, I believe so. I can understand that the platform could have some trouble with it, if it doesn't recognize that the template parameter DerivA in shared_ptr<DerivA> is derived of BaseA.
|
You have a point, but it would require changing the specs of shared_ptr.
To simplify the exposition, let us consider the following class:
template < typename T > struct pointer_to {
T * the_ptr;
pointer_to ( T * ptr ) : the_ptr ( ptr ) {}
T & operator* ( void ) const { return ( *the_ptr ); }
T * operator-> ( void ) const { return ( the_ptr ); }
template < typename D > pointer_to ( pointer_to<D> const & d_pointer ) : the_ptr ( d_pointer.the_ptr ) {}
};
There is a conversion operator that allows to copy construct a pointer_to<T> from a pointer to any derived class. Attempts to copy construct from non-derived classes will fail when the compiler encounters the body of the conversion operator. With this setup, the following will be ambiguous:
struct X {}; struct XD : public X {}; struct Y {}; struct YD : public Y {};
void f ( pointer_to<X> xp ) {} void f ( pointer_to<Y> yp ) {}
int main ( void ) { pointer_to<YD> ydp ( new YD ); f( ydp ); }
The reason is that the compiler sees two possible conversions and it is not supposed to check whether only one of them can be compiled cleanly.
Now, there is a way to guide the compiler in these issues. But it requires some serious scaffolding:
struct yes_type { char dummy; }; struct no_type { yes_type a; yes_type b; };
template < typename From, typename To > class is_convertible {
static From* dummy ( void );
static yes_type check ( To );
static no_type check ( ... );
public:
static bool const value = sizeof( check( *dummy() ) ) == sizeof( yes_type );
}; // is_convertible
template < bool b, typename T > struct enable_if;
template < typename T > struct enable_if<true,T> { typedef T type; };
template < typename T > struct pointer_to {
T * the_ptr;
pointer_to ( T * ptr ) : the_ptr ( ptr ) {}
T & operator* ( void ) const { return ( *the_ptr ); }
T * operator-> ( void ) const { return ( the_ptr ); }
template < typename D > pointer_to ( pointer_to<D> const & d_pointer, typename enable_if< is_convertible<D*,T*>::value, void*
: the_ptr ( d_pointer.the_ptr ) {}
};
With this setup, the above snippet will compile cleanly since the signature of the conversion operator is enough to tell the compiler that there is only one possible conversion.
--------------- End Quote ----------------
So, it would appear that: - there is a conceptual difference between pointer and shared_ptr - this difference can be removed with the proposed solution, allowing greater compatibility and usability of shared_ptr - but that would require changing the specs of shared_ptr.
Rumor has is that part of the discussion on standadization is now taking place here, so at least I would like to bring the matter to attention - although I have no idea whose attention :-)
Best regards, F. Beekhof
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Peter Dimov |  |
| Posted: Sun Aug 31, 2008 8:58 am Post subject: Re: Specs for shared_ptr; compatibility between classic poin |  |
| |  | |
Fokko Beekhof wrote: ....
| Quote: | When we replace classical pointers with shared_ptrs, it does not work anymore: .... |
| Quote: | struct S { S(std::tr1::shared_ptr<BaseA> pa_) : pa(pa_) {} S(std::tr1::shared_ptr<BaseB> pb_) : pb(pb_) {}
std::tr1::shared_ptr<BaseA> pa; std::tr1::shared_ptr<BaseB> pb; };
int main() { // S s(std::tr1::shared_ptr<BaseA>(new DerivA()) ); // works // S s( new DerivA() ); // Doesn't work, SP constructor is explicit S s(std::tr1::shared_ptr<DerivA>(new DerivA()) ); // breaks
return 0; } .... |
Your code works with the latest version of boost::shared_ptr; it uses essentially the same technique that Kai-Uwe Bux outlined.
| Quote: | Rumor has is that part of the discussion on standadization is now taking place here, so at least I would like to bring the matter to attention - although I have no idea whose attention 
|
The specification of std::shared_ptr in the latest draft standard has already been changed to account for this conversion ambiguity. Your post has prompted me to take a second look, though, and I think that your code is actually not covered. I'll make sure it's fixed. Thanks for bringing it up.
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Alberto Ganesh Barbati |  |
| Posted: Sun Aug 31, 2008 10:49 pm Post subject: Re: Specs for shared_ptr; compatibility between classic poin |  |
| |  | |
Fokko Beekhof ha scritto:
| Quote: | Hello,
recently, I posted a question about a difference in behaviour between regular pointers and shared_ptrs on comp.lang.c++ . Maybe the outcome of the conversation is relevant to someone here, so posting a summary here.
snip
So, it would appear that: - there is a conceptual difference between pointer and shared_ptr
|
Of course and I see no problem with that.
| Quote: | - this difference can be removed with the proposed solution, allowing greater compatibility and usability of shared_ptr - but that would require changing the specs of shared_ptr.
Rumor has is that part of the discussion on standadization is now taking place here, so at least I would like to bring the matter to attention - although I have no idea whose attention 
|
The shared_ptr specs in the most recent draft for C++0x (which include substantial differences with respect to the TR1 specs) already provides the feature you described.
HTH,
Ganesh
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
|
|