简体   繁体   English

Equality-compare std :: weak_ptr

[英]Equality-compare std::weak_ptr

I want to compare two std::weak_ptr's or one std::weak_ptr and one std::shared_ptr for equality. 我想比较两个std :: weak_ptr或一个std :: weak_ptr和一个std :: shared_ptr的相等性。

What I want to know is whether the object each of the weak_ptr's/shared_ptr's point to is the same. 我想知道的是weak_ptr / shared_ptr指向的每个对象是否相同。 The comparison should yield negative results not just if the addresses don't match, but also if the underlying object was deleted and then reconstructed with the same address by chance. 比较应该产生负面结果,不仅如果地址不匹配,而且如果基础对象被删除然后偶然使用相同地址重建。

So basically, I want this assertion to hold even if the allocator reserves the same address: 所以基本上,即使分配器保留相同的地址,我也希望这个断言成立:

auto s1 = std::make_shared<int>(43);
std::weak_ptr<int> w1(s1);

s1.reset();

auto s2 = std::make_shared<int>(41);
std::weak_ptr<int> w2(s2);

assert(!equals(w1,w2));

The weak_ptr templates do not provide equality operators, and as I understood that's for a good reason . weak_ptr模板不提供相等的运算符,正如我所理解的那样,这是有充分理由的

So a naive implementation would look like this: 所以一个天真的实现看起来像这样:

template <typename T, typename U>
inline bool naive_equals(const std::weak_ptr<T>& t, const std::weak_ptr<U>& u)
{
    return !t.expired() && t.lock() == u.lock();
}

template <typename T, typename U>
inline bool naive_equals(const std::weak_ptr<T>& t, const std::shared_ptr<U>& u)
{
    return !t.expired() && t.lock() == u;
}

If the first weak_ptr expired in the meantime, it yields 0. If not, I upgrade the weak_ptr to a shared_ptr and compare the addresses. 如果第一个weak_ptr在此期间到期,则它会产生0.如果不是,我将weak_ptr升级为shared_ptr并比较地址。

The problem with this is that i have to lock the weak_ptr's twice (once)! 这个问题是我必须锁定weak_ptr两次(一次)! I'm afraid that takes too much time. 我担心花费太多时间。

I came up with this: 我想出了这个:

template <typename T, typename U>
inline bool equals(const std::weak_ptr<T>& t, const std::weak_ptr<U>& u)
{
    return !t.owner_before(u) && !u.owner_before(t);
}


template <typename T, typename U>
inline bool equals(const std::weak_ptr<T>& t, const std::shared_ptr<U>& u)
{
    return !t.owner_before(u) && !u.owner_before(t);
}

Which checks if the owner block of u is not "before" t's and t's not before u's, so t == u. 检查u的所有者块是否在“t”之前并且t不在u之前,所以t == u。

Does this work as I intend it? 这是否符合我的意图? Do two weak_ptr's created from distinct shared_ptr's always compare as non-equal this way? 从不同的shared_ptr创建的两个weak_ptr总是以这种方式比较为不相等吗? Or did I miss something? 还是我错过了什么?

Edit: Why do I want to do this in the first place? 编辑:我为什么要首先这样做? I want to have a container with shared pointers, and I want to hand out references to the objects in it. 我想要一个带有共享指针的容器,我想分发对它中对象的引用。 I can't use iterators, since they may be invalidated. 我不能使用迭代器,因为它们可能无效。 I could hand out (integer) ID's, but that leads to problems with uniqueness and would require a map type and complicate search/insertion/removal operations. 我可以分发(整数)ID,但这会导致唯一性问题,并且需要地图类型和复杂的搜索/插入/删除操作。 The idea is to use a std::set and give out the pointers themselves (capsuled in a wrapper class) as keys, so that clients can use the weak_ptr's to access the objects in the set. 我们的想法是使用std :: set并将指针本身(在包装类中封装)作为键给出,以便客户端可以使用weak_ptr来访问集合中的对象。

Completely rewriting this answer because I totally misunderstood. 完全重写这个答案,因为我完全误解了。 This is a tricky thing to get right! 要做对,这是一个棘手的事情!

The usual implementation of std::weak_ptr and std::shared_ptr that is consistent with the standard is to have two heap objects: the managed object, and a control block . std::shared_ptr一致的std::weak_ptrstd::shared_ptr的通常实现是拥有两个堆对象:托管对象和控制块 Each shared pointer that refers to the same object contains a pointer to the object and to the control block, and each weak pointer likewise. 引用同一对象的每个共享指针包含指向对象和控制块的指针,同样包含每个弱指针。 The control block keeps a record of the number of shared pointers and the number of weak pointers, and deallocates the managed object when the number of shared pointers reaches 0; 控制块记录共享指针的数量和弱指针的数量,并在共享指针的数量达到0时解除分配管理对象; the control block itself is deallocated when the number of weak pointers also reaches 0. 当弱指针的数量也达到0时,控制块本身被释放。

This is complicated by the fact that the object pointer in a shared or weak pointer can point to a subobject of the actual managed object, eg a base class, a member, or even another heap object that is owned by the managed object. 由于共享或弱指针中的对象指针可以指向实际托管对象的子对象,例如基类,成员,甚至是托管对象拥有的另一个堆对象,因此这很复杂。

S0 ----------______       MO <------+
   \__             `----> BC        |
      \_ _______--------> m1        |
     ___X__               m2 --> H  |
S1 -/      \__ __----------------^  |
    \___ _____X__                   |
    ____X________\__                |
W0 /----------------`---> CB -------+  
                          s = 2 
                          w = 1 

Here we have two shared pointers pointing respectively to a base class of the managed object and to a member, and a weak pointer pointing to a heap object owned by the managed object; 这里我们有两个共享指针,分别指向托管对象和成员的基类,以及指向托管对象拥有的堆对象的弱指针; the control block records that two shared pointers and one weak pointer exist. 控制块记录存在两个共享指针和一个弱指针。 The control block also has a pointer to the managed object, which it uses to delete the managed object when it expires. 控制块还有一个指向托管对象的指针,用于在托管对象过期时删除托管对象。

The owner_before / owner_less semantics are to compare shared and weak pointers by the address of their control block, which is guaranteed not to change unless the pointer itself is modified; owner_before / owner_less语义是通过控制块的地址来比较共享和弱指针,除非指针本身被修改,否则保证不会改变; even if a weak pointer expires because all shared pointers have been destructed, its control block still exists until all weak pointers have also been destructed. 即使弱指针因为所有共享指针都被破坏而到期 ,它的控制块仍然存在,直到所有弱指针都被破坏为止。

So your equals code is absolutely correct and thread safe. 所以你的equals代码绝对正确且线程安全。

The issue is that it's not consistent with shared_ptr::operator== because that compares the object pointers, and two shared pointers with the same control block can point to different objects (as above). 问题是它与shared_ptr::operator==不一致,因为它比较了对象指针,并且具有相同控制块的两个共享指针可以指向不同的对象(如上所述)。

For consistency with shared_ptr::operator== , writing t.lock() == u will be absolutely fine; 为了与shared_ptr::operator==保持一致,写t.lock() == u绝对没问题; note however that if it returns true then it's still not definite that the weak pointer is a weak pointer of the other shared pointer; 但请注意,如果它返回true那么仍然不能确定弱指针是另一个共享指针的弱指针; it could be an alias pointer and so could still expire in following code. 可能是一个别名指针,所以仍然可以在下面的代码中过期。

However, comparing control blocks has less overhead (because it doesn't need to look at the control block) and will give the same results as == if you're not using alias pointers. 但是,比较控制块的开销较小(因为它不需要查看控制块),并且如果您不使用别名指针,则会给出与==相同的结果。


I think that there's something of a deficiency in the standard here; 我认为这里的标准有些不足之处; adding an owner_equals and owner_hash would allow using weak_ptr in unordered containers, and given owner_equals it actually becomes sensible to compare weak pointers for equality, as you can safely compare the control block pointer then the object pointer, since if two weak pointers have the same control block then you know that either both or neither are expired. 添加owner_equalsowner_hash将允许在无序容器中使用weak_ptr ,并且给定owner_equals实际上比较弱指针的相等性是合理的,因为你可以安全地比较控制块指针然后比较对象指针,因为如果两个弱指针具有相同的控制阻止然后你知道两者或两者都没有过期。 Something for the next version of the standard, perhaps. 也许是下一版标准的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM