So I have a std::vector<std::shared_ptr<T>> myListOfT;
and I have a std::weak_ptr<T> ptrToOneT;
that was created from one of the pointers used to fill that container (say I have it inside a callback function). Will std::find
on that container and my weak_ptr
give me an iterator to the original shared_ptr
(if such one exists in the collection)? Is it guaranteed somewhere in standard or is this implementation dependent?
-
The short answer is yes but you may need to cast the values returned by `get()` to the absolute base pointer before doing the comparison. – Captain Obvlious Jun 14 '15 at 13:13
-
4Obtain a `shared_ptr` with `weak_ptr::lock()`, then pass that to `std::find`. You'll find some pointer referring to the same object. – Igor Tandetnik Jun 14 '15 at 13:14
-
Also, by locking, you prevent the object the weak_ptr point to from being deleted if the reference count goes to zero eg. in another thread. – Alexandre C. Jun 14 '15 at 13:18
-
What @IgorTandetnik said. When you have a `weak_ptr`, then you may or may not be able to get a `shared_ptr` from it with `.lock()`. There is no point searching to find the pointer. If you want the iterator, lock the `weak_ptr` and search for the resulting `shared_ptr`. – Kent Jun 14 '15 at 13:19
-
@IgorTandetnik Answers in comments! – Barry Jun 14 '15 at 14:09
3 Answers
We can get away without locking the weak_ptr
by using std::weak_ptr::owner_before
. I'll use a slightly more verbose solution than necessary and introduce owner_equal
, which is the counterpart to std::owner_less
:
template<typename T>
class owner_equal
{
private:
template<typename L, typename R>
static bool e(L const& l, R const& r)
{ return !(l.owner_before(r)) && !(r.owner_before(l)); }
public:
using S = std::shared_ptr<T>;
using W = std::weak_ptr<T>;
bool operator()(S const& l, W const& r) const { return e(l, r); }
bool operator()(W const& l, S const& r) const { return e(l, r); }
};
With this function object type, we can customize std::find_if
:
using T = int;
std::vector<std::shared_ptr<T>> myListOfT =
{std::make_shared<int>(0), std::make_shared<int>(1), std::make_shared<int>(2)};
int const pos = 1;
std::weak_ptr<T> ptrToOneT = myListOfT[pos];
auto pred = [&ptrToOneT](std::shared_ptr<T> const& e)
{ return owner_equal<T>{}(e, ptrToOneT); };
auto const r = std::find_if(begin(myListOfT), end(myListOfT), pred);
assert(r - begin(myListOfT) == pos);
The lambda can be replaced by a bind-expression such as:
auto pred = std::bind(owner_equal<T>{}, std::cref(ptrToOneT),
std::placeholders::_1);
@davidhigh suggested an optimization:
template<typename FwdIt, typename T>
FwdIt findWeakPtr(FwdIt b, FwdIt e, std::weak_ptr<T> const& w)
{
if(w.expired()) return e;
else
{
auto pred = [&w](std::shared_ptr<T> const& e)
{ return owner_equal<T>{}(e, w); };
return std::find_if(b, e, pred);
}
}
(not tested)
This also slightly changes the behaviour: If the weak_ptr
is "empty", e.g. having been created from an empty shared_ptr
or via the default ctor, it will compare equal to any empty shared_ptr
via owner_equal
. However, weak_ptr::expired
is true in that case. Therefore, the optimized version will not find empty shared pointers in the range.
Should empty shared pointers be found in the range?
Consider:
using T = int;
std::vector<std::shared_ptr<T>> myListOfT =
{std::shared_ptr<T>(), std::shared_ptr<T>()};
int const pos = 1;
std::weak_ptr<T> ptrToOneT = myListOfT[pos];
auto const r = my_weak_ptr_find(begin(myListOfT), end(myListOfT), ptrToOneT);
auto const r_pos = r - begin(myListOfT);
Empty shared pointers are equal. Therefore, if you allow finding empty shared pointers, r_pos != pos && r != end(myListOfT)
is possible. For example, the first version of the algorithm in this answer yields r_pos == 0
.
For additional context, see:
-
1+1, nice find, but I think you could bypass the whole search if the weak_ptr is expired? This one would get away with O(1) instead of O(N). – davidhigh Jun 14 '15 at 14:49
-
2@davidhigh Interesting. It might be worth though to measure it at least for small ranges, since I guess `expired` requires an atomic read. – dyp Jun 14 '15 at 14:58
-
1As far I understood your approach, my comment holds only if you want to drop the `nullptr == nullptr` hits from the search (?) -- which is at least what I would intuitively want ... others might not, and then this is clearly advantageous. – davidhigh Jun 14 '15 at 15:11
-
@davidhigh Hmmm true. For some reason, I thought there could be no empty `weak_ptr`s (only expired ones). – dyp Jun 14 '15 at 15:25
std::weak_ptr::lock()
is how you "promote" a weak_ptr
to a shared_ptr
:
std::weak_ptr<T> ptrToOneT;
auto observe = ptrToOneT.lock();
if (observe) {
// observe now shares ownership of the one T
}
else {
// there is no managed object or it has already
// been destroyed
}
If lock()
succeeds, then you have a normal std::shared_ptr<T>
that you can use to find()
like you would any other object in a container. Though you may not need to find()
it since you already have it (unless you want to erase()
it or something).
Side-note, with shared_ptr
, it's not really meaningful to refer to the "original shared_ptr
"

- 286,269
- 29
- 621
- 977
I had a similar problem a few days ago in my own code. According to my SO-research, it is possible to do what you asked for. Lock the weak-pointer and if the shared-pointer is not-expired, then use std::find
struct A{};
int main()
{
std::vector<std::shared_ptr<A> > sptr_vec;
std::weak_ptr<A> wptr;
if(auto sptr = wptr.lock())
{
auto it = std::find(std::begin(sptr_vec), std::end(sptr_vec), sptr);
if (it != std::end(sptr_vec))
{
std::cout<<"found"<<std::endl;
}
}
}
Note that the C++ standard itself is not that relevant here -- the heart of the comparison of the shared-pointers is a comparison of the contained raw pointers, i.e. addresses in memory are compared.
Alternatively, if you have a vector of weak-pointers, you could use std::find_if
with a predicate that locks on the fly:
std::vector<std::weak_ptr<A> > wptr_vec;
std::shared_ptr<A> sptr;
auto it = std::find_if(std::begin(wptr_vec), std::end(wptr_vec)
, [&sptr](auto const& w){ auto s = w.lock();
if (s) {return s == sptr;}
return false; });
if (it != std::end(wptr_vec))
{
std::cout<<"found"<<std::endl;
}
Note that fot this application, I would consider the equivalence of nullptr
with itself, i.e. that nullptr == nullptr
is true
, as unwanted. Thus I excluded this case from the predicate (and also from the search in the first code block).
EDIT: just considered the owner_lock
solution by @dyp, which is advantageous if it is just about the search.

- 14,652
- 2
- 44
- 75