c++ Equality-compare std :: weak_ptr

我想比较两个std :: weak_ptr或一个std :: weak_ptr和一个std :: shared_ptr的相等性。

我想知道的是,每个weak_ptr的/ shared_ptr的对象是否相同。
比较应该产生不利的结果,不仅地址不匹配,而且如果基础对象被删除,然后重新用相同的地址偶然地重建。

所以基本上,即使分配器保留相同的地址,我也想要这个断言:

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

weak_ptr模板不提供相等的运算符,据了解,这是for a good reason

所以一个天真的实现将如下所示:

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

如果第一个weak_ptr在同一时间过期,则会产生0.如果没有,我将weak_ptr升级到shared_ptr并比较地址。

这个问题是我必须锁定weak_ptr两次(一次)!恐怕需要太多的时间。

我想出了这个:

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

哪个检查你的所有者块是不是“之前”和t不在你的之前,所以t ==你。

这是否按照我的意图工作?从不同的shared_ptr创建的两个weak_ptr是否总是以不相等的方式比较?
还是我想念的东西?

编辑:为什么要这样做呢?
我想要一个带有共享指针的容器,我想发送对它的对象的引用。
我不能使用迭代器,因为它们可能无效。我可以发出(整数)ID,但这会导致唯一性问题,并且需要一个地图类型,并使搜索/插入/删除操作变得复杂。
这个想法是使用std :: set并将指针本身(封装在包装器类中)作为键,以便客户端可以使用weak_ptr来访问集合中的对象。

最佳答案
完全重写这个答案,因为我完全误解了。这是一个棘手的事情,以获得正确的!

与标准一致的std :: weak_ptr和std :: shared_ptr的通常实现是具有两个堆对象:被管理对象和一个控制块。引用同一对象的每个共享指针都包含指向对象和控件块的指针,以及每个弱指针。控制块保存共享指针数量和弱指针数量的记录,并且当共享指针数达到0时释放被管理对象;当弱指针的数量也达到0时,控制块本身被释放。

这通过共享或弱指针中的对象指针可以指向实际管理对象的子对象的事实而变得复杂,例如,一个基类,一个成员,或甚至另一个被托管对象拥有的堆对象。

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

这里我们有两个共享指针分别指向托管对象和成员的基类,以及指向托管对象拥有的堆对象的弱指针;控制块记录存在两个共享指针和一个弱指针。控制块还有一个指向托管对象的指针,它用于在托管对象过期时删除它。

owner_before / owner_less语义是将共享和弱指针与其控制块的地址进行比较,除非指针本身被修改,否则保证不改变;即使一个弱指针由于所有共享指针已被破坏而过期,其控制块仍然存在,直到所有弱指针也被破坏为止。

所以你的等号代码绝对正确,线程安全。

问题是它与shared_ptr :: operator ==不一致,因为它比较了对象指针,并且具有相同控件块的两个共享指针可以指向不同的对象(如上所述)。

为了与shared_ptr :: operator ==的一致性,写t.lock()== u将是绝对好的;但是请注意,如果它返回true,则弱指针是另一个共享指针的弱指针仍然不明确;它可能是一个别名指针,因此可能会在以下代码中过期。

然而,比较控制块具有较少的开销(因为它不需要查看控制块),并且将给出与==相同的结果,如果你不使用别名指针。

我认为这里的标准有缺点;添加owner_equals和owner_hash将允许在无序容器中使用weak_ptr,给定owner_equals,比较弱指针的相等性实际上变得明智,因为您可以安全地比较控件块指针,然后是对象指针,因为如果两个弱指针具有相同的控件然后你知道两者都没有过期。也许是下一个版本的标准的东西。

转载注明原文:c++ Equality-compare std :: weak_ptr - 代码日志