
I ran into a situation (see my "smart list" post on the boost users group) where I wanted to create a custom allocator whose pointer typedef is boost::shared_ptr. In other words, the code would look something like: template <typename T> class my_allocator { public: typedef boost::shared_ptr<T> pointer; }; The problem with doing this is that boost::shared_ptr<T> does not have the same "interface" as the equalivalent raw pointer type T*. (I use the term interface in the generic programming/template sense; not in the object oriented sense.) One example is that there is no operator= on boost::shared_ptr that takes a raw pointer; instead reset() must be used. Here are my questions: (1) What advantage does this change of interface serve that makes it worth breaking potential operations such as that above? (2) Does anyone have ideas on how to get around this problem? I toyed with the idea of writing a class that derives from boost::shared_ptr and has the interface that I desire -- anybody have thoughts on this? Thanks, David M. Jones

David M. Jones wrote:
I ran into a situation (see my "smart list" post on the boost users group) where I wanted to create a custom allocator whose pointer typedef is boost::shared_ptr. In other words, the code would look something like:
template <typename T> class my_allocator { public: typedef boost::shared_ptr<T> pointer; };
The problem with doing this is that boost::shared_ptr<T> does not have the same "interface" as the equalivalent raw pointer type T*. (I use the term interface in the generic programming/template sense; not in the object oriented sense.) One example is that there is no operator= on boost::shared_ptr that takes a raw pointer; instead reset() must be used.
Raw pointers don't necessarily have an operator= that takes another kind of raw pointer. For example, T __near * cannot be assigned T*, only itself. Similarly, shared_ptr<T> cannot be assigned T*, only itself. If a component tries to assign a T* to allocator<T>::pointer, it is broken WRT nonstandard pointers.

"Peter Dimov" <pdimov@mmltd.net> wrote in message news:002e01c4b37d$cfac5c80$6401a8c0@pdimov2...
David M. Jones wrote:
I ran into a situation (see my "smart list" post on the boost users group) where I wanted to create a custom allocator whose pointer typedef is boost::shared_ptr. In other words, the code would look something like:
template <typename T> class my_allocator { public: typedef boost::shared_ptr<T> pointer; };
The problem with doing this is that boost::shared_ptr<T> does not have the same "interface" as the equalivalent raw pointer type T*. (I use the term interface in the generic programming/template sense; not in the object oriented sense.) One example is that there is no operator= on boost::shared_ptr that takes a raw pointer; instead reset() must be used.
Raw pointers don't necessarily have an operator= that takes another kind of raw pointer. For example, T __near * cannot be assigned T*, only itself. Similarly, shared_ptr<T> cannot be assigned T*, only itself.
If a component tries to assign a T* to allocator<T>::pointer, it is broken WRT nonstandard pointers.
Thank you for your reply, Peter. Let me be more specific about the problem I ran into. I am using MSVC 7.1 and the implementation of the STL that comes with it. When I tried to create and use an object of type std::list<int, my_allocator<int> >, I received a compiler error because the STL code contains _Myhead = 0; where _Myhead is of type my_allocator<node>::pointer (ie. boost::shared_ptr<node>) and node is the class that defines the nodes in the linked list. So the STL is not calling operator=() for a boost::shared_ptr with just any pointer type on the right-hand side. In fact, it's not even a T* -- it is specially setting it to null. Are you telling me that this STL implementation is (slightly) invalid? Or are you telling me that I should not expect boost::shared_ptr to be a valid type for use as a custom allocator::pointer? Or both? I have read that Scott Meyers (Effective STL; Item 10) says that custom allocators should always have typedef T* pointer but I have also read that Matt Austern (December 2000 CUJ column) says that custom allocators can sometimes be "some pointer-like class"; it seems to me that boost:shared_ptr is a "pointer-like class".

David M. Jones wrote:
Thank you for your reply, Peter.
Let me be more specific about the problem I ran into. I am using MSVC 7.1 and the implementation of the STL that comes with it. When I tried to create and use an object of type std::list<int, my_allocator<int> >, I received a compiler error because the STL code contains _Myhead = 0; where _Myhead is of type my_allocator<node>::pointer (ie. boost::shared_ptr<node>) and node is the class that defines the nodes in the linked list.
So the STL is not calling operator=() for a boost::shared_ptr with just any pointer type on the right-hand side. In fact, it's not even a T* -- it is specially setting it to null.
Are you telling me that this STL implementation is (slightly) invalid? Or are you telling me that I should not expect boost::shared_ptr to be a valid type for use as a custom allocator::pointer? Or both?
I have read that Scott Meyers (Effective STL; Item 10) says that custom allocators should always have typedef T* pointer but I have also read that Matt Austern (December 2000 CUJ column) says that custom allocators can sometimes be "some pointer-like class"; it seems to me that boost:shared_ptr is a "pointer-like class".
The Standard says (in Table 32, Allocator requirements) that Allocator::pointer must be a "pointer to T", but doesn't go into more detail. It is not clear what requirements must a type P meet in order to be considered a valid "pointer to T". The Standard also says in 20/4 that implementations are permitted to assume that Allocator::pointer is T*. 20/5 goes on to inform us that implementors are encouraged to support "more general memory models" and that in those cases the semantics are implementation defined. "Implementation defined" means that they can do whatever they choose, as long as it is documented. So. This STL implementation is valid. It makes an attempt to support ::pointer types that are not T*. However, it places a requirement on these types to support the expression p = 0; which shared_ptr does not. It is possible to add this capability to shared_ptr, but it's also possible to rewrite the STL so that it uses the alternate syntax: p = pointer(); thereby relaxing the requirements placed on ::pointer types. From where I sit, the second option seems more "correct", but the STL authors may not agree. As to your question:
Or are you telling me that I should not expect boost::shared_ptr to be a valid type for use as a custom allocator::pointer?
Well, I don't know. It might or might not work, depending on how you use it and what else implicit requirements the STL has. Try adding shared_ptr & operator=(unspecified_bool_type) { reset(); return *this; } to your copy of shared_ptr.hpp and see what happens.

"David M. Jones" <djones@keymark.com> writes:
Are you telling me that this STL implementation is (slightly) invalid? Or are you telling me that I should not expect boost::shared_ptr to be a valid type for use as a custom allocator::pointer?
It is the latter. STL implementations are permitted to assume that an allocator for a type T have pointer and reference types T* and T& respectively. 20.1.5 4 Implementations of containers described in this International Standard are permitted to assume that their Allocator template parameter meets the following two additional requirements beyond those in Table 32. --- All instances of a given allocator type are required to be interchangeable and always compare equal to each other. --- The typedef members pointer, const_pointer, size_type, and difference_type are required to be T*, T const*, size_t, and ptrdiff_t, respectively. This is sometimes known among committee members as "the weasel wording." -- Dave Abrahams Boost Consulting http://www.boost-consulting.com
participants (3)
-
David Abrahams
-
David M. Jones
-
Peter Dimov