对象std :: shared_ptr是否可以通过std :: weak_ptr找到?

时间:2015-06-14 13:05:54

标签: c++ c++11 stl shared-ptr weak-ptr

所以我有std::vector<std::shared_ptr<T>> myListOfT;并且我有一个std::weak_ptr<T> ptrToOneT;是从用于填充该容器的指针之一创建的(比如我在回调函数中有它)。该容器上的std::findweak_ptr的{​​{1}}会给我一个原始shared_ptr的迭代器(如果集合中存在这样的那个)吗?它是否在标准的某处保证或者这种实现是否依赖?

3 个答案:

答案 0 :(得分:3)

我们可以通过使用weak_ptr在锁定std::weak_ptr::owner_before的情况下离开。我将使用比必要更详细的解决方案并引入owner_equal,这是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); }
};

使用此功能对象类型,我们可以自定义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);

lambda可以用bind-expression替换,例如:

auto pred = std::bind(owner_equal<T>{}, std::cref(ptrToOneT),
                      std::placeholders::_1);

@davidhigh建议进行优化:

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);
    }
}

(未经测试)

这也会略微改变行为:如果weak_ptr为“空”,例如使用空shared_ptr或默认ctor创建,它将通过shared_ptr与任何空owner_equal进行比较。但是,weak_ptr::expired在这种情况下是正确的。因此,优化版本将不会在该范围内找到空的共享指针。

是否应在范围内找到空的共享指针?

考虑:

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);

空的共享指针相等。因此,如果您允许查找空的共享指针,则r_pos != pos && r != end(myListOfT)是可能的。例如,此答案中的第一个算法版本产生r_pos == 0

有关其他背景信息,请参阅:

答案 1 :(得分:1)

std::weak_ptr::lock()就是你&#34;推广&#34; weak_ptrshared_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
}

如果lock()成功,那么您可以像使用容器中的任何其他对象一样使用std::shared_ptr<T> find()。虽然你可能不需要find()它,因为你已经拥有它(除非你想要erase()它或者什么)。

附注,shared_ptr,引用&#34;原始shared_ptr&#34;并没有多大意义。

答案 2 :(得分:1)

我几天前在自己的代码中遇到过类似的问题。根据我的SO研究,你可以做你想要的。锁定弱指针,如果共享指针未过期,则使用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;
        }
    }
}

请注意,C ++标准本身并不重要 - 共享指针比较的核心是对包含的原始指针进行比较,即比较内存中的地址。

或者,如果你有一个弱指针向量,你可以使用std::find_if和一个即时锁定的谓词:

    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;
    }

请注意,在这个应用程序中,我会考虑nullptr与其自身的等效性,即nullptr == nullptrtrue,这是不需要的。因此,我从谓词(以及第一个代码块中的搜索)中排除了这种情况。

编辑:刚刚考虑了@dyp的owner_lock解决方案,如果仅仅是搜索,这是有利的。