This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
[c++0x] shared_ptr get() and use_count() inconsistency
- From: Rodolfo Lima <rodolfo at rodsoft dot org>
- To: libstdc++ at gcc dot gnu dot org
- Date: Thu, 05 Jun 2008 01:42:25 -0300
- Subject: [c++0x] shared_ptr get() and use_count() inconsistency
Hi, I've been playing with shared_ptr and its aliasing constructor and
the following code doesn't work as expected:
int main()
{
std::shared_ptr<int> aux;
int i;
std::shared_ptr<int> aux2(aux, &i);
assert(aux2.use_count() == 0);
assert((bool)aux2, false); // assert failure here
assert(aux2.get() == NULL); // assert failure here
}
I know there's no C++0x standard yet, blah blah blah, but in current
libstdc++ implementation shared_ptr's get returns something different
than NULL even when use_count()==0.
The current standard draft says that use_count() should return 0 if the
shared_ptr is empty, and get() should return NULL if the shared_ptr is
also empty.
The current implementation simply returns the stored pointer, whether
use_count() is 0 or not.
I've attached a patch with two possible solutions. The first
(shared_ptr.diff) just checks if use_count()==0 before returning the
stored pointer, if it is, returns NULL. Is also changes operator-> and
operator* to use get() with the correct semantics. This problem is that
this solution incurs some overhead, with an addition of an comparison.
The second solution (shared_ptr_fast.diff) sets the stored pointer to
NULL in the aliasing ctor if the shared_ptr to be aliased has
use_count()==0. AFAIK, the semantics is the same of the last solution,
but this one is faster because the comparison is done just one time. I
didn't check if this has bad side effects I didn't think of.
Regards,
Rodolfo Lima.
--- boost_shared_ptr.h 2008-03-17 23:36:08.000000000 -0300
+++ boost_shared_ptr_new.h 2008-06-05 01:31:14.000000000 -0300
@@ -460,20 +460,20 @@
#endif
operator*() const // never throws
{
- _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
- return *_M_ptr;
+ _GLIBCXX_DEBUG_ASSERT(use_count() != 0);
+ return *get();
}
_Tp*
operator->() const // never throws
{
- _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
- return _M_ptr;
+ _GLIBCXX_DEBUG_ASSERT(use_count() != 0);
+ return get();
}
_Tp*
get() const // never throws
- { return _M_ptr; }
+ { return use_count()!=0?_M_ptr:0; }
// Implicit conversion to "bool"
private:
@@ -481,7 +481,7 @@
public:
operator __unspecified_bool_type() const // never throws
- { return _M_ptr == 0 ? 0 : &__shared_ptr::_M_ptr; }
+ { return get() == 0 ? 0 : &__shared_ptr::_M_ptr; }
bool
unique() const // never throws
--- boost_shared_ptr.h 2008-03-17 23:36:08.000000000 -0300
+++ boost_shared_ptr_new.h 2008-06-05 01:36:10.000000000 -0300
@@ -291,8 +291,13 @@
*/
template<typename _Tp1>
__shared_ptr(const __shared_ptr<_Tp1, _Lp>& __r, _Tp* __p)
- : _M_ptr(__p), _M_refcount(__r._M_refcount) // never throws
- { }
+ : _M_refcount(__r._M_refcount) // never throws
+ {
+ if(__r.use_count() != 0)
+ _M_ptr = __p;
+ else
+ _M_ptr = 0;
+ }
#endif
// generated copy constructor, assignment, destructor are fine.