Pete said:
This code doesn't illustrate why this distinction is useful. A more
idiomatic way to write it would be:
shared_ptr<T> sp(wp);
if (!sp)
// Act as if a null pointer was passed
Consider the non-smart-pointer version of a function:
void f(T *p)
{
if (p)
{
// Case 1: Do one thing.
// Case 2: Undefined behavior because p isn't valid.
}
else
{
// Case 3: Do another thing.
}
}
The obvious solution to avoiding Case 2 above is to simply not pass an
invalid pointer. But that isn't always easy. Consider this motivating
example. You have a server that allows multiple users to connect.
While connected, they may schedule events to occur at some time in the
future. Your "event" objects store a pointer to the user that scheduled
them. What should you do, though, if a user schedules an event to be
processed 30 minutes from now, and then immediately disconnects?
A lot of solutions come to mind, but a particularly simple one is to
have your events store a weak_ptr to the scheduling user, and then find
out about invalid users in Case 2. (Yes, in this contrived example you
could distinguish Case 2 from Case 3 prior to calling f, but that isn't
the point.)
The question is how to distinguish "a weak_ptr to nothing" (Case 3) from
"a weak_ptr to something invalid" (Case 2).
Constructing a shared_ptr won't work. Consider the following fragment,
in which BOTH constructions of a shared pointer will throw bad_weak_ptr.
weak_ptr<T> wp1, wp2, wp3 ;
{
shared_ptr<T> temp_sp(new T) ;
wp1 = temp_sp ;
wp2 = temp_sp ;
// Case 1: wp1 is a valid weak_ptr
share_ptr<T> sp1(wp1) ;
}
// Case 2: wp2 is an invalid weak_ptr
shared_ptr<T> sp2(wp2) ;
// Case 3: wp3 is a valid weak_ptr, but pointing to nothing.
shared_ptr<T> sp3(wp3) ;
The use_count() member is equally problematic, as it returns 0 for both
an invalid weak_ptr and a weak_ptr to nothing. Likewise, lock() will
return an empty shared_ptr in both cases. The only way I've found that
does seem to work is something like:
bool is_empty(weak_ptr<T> wp)
{
weak_ptr<T> empty ;
// operator < claims to establish a strict weak order, so this
// should always work.
return (!(wp < empty) && !(empty < wp)) ;
}
It is my opinion that throwing bad_weak_ptr when constructing a
shared_ptr from an empty weak_ptr is a design flaw. The "proper" thing
to do would be to construct an empty shared_ptr.