我想要比较两个std::weak_ptr或一个std::weak_ptr和一个std::shared_ptr是否相等。
我想要知道的是,这两个weak_ptr/shared_ptr所指向的对象是否相同。 比较结果应该是负的,不仅当地址不匹配时,还应该在底层对象被删除后,通过偶然的方式重新构造了相同地址的情况下也是负的。
所以基本上,我希望即使分配器保留相同的地址,这个断言也能成立:
weak_ptr模板没有提供相等运算符,而我理解这是有充分理由的。
因此,一个简单的实现看起来像这样:
如果第一个weak_ptr在此期间过期,它将返回0。如果没有过期,我将将weak_ptr升级为shared_ptr并比较地址。
这种方法的问题在于我必须两次锁定weak_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模板没有提供相等运算符,而我理解这是有充分理由的。
因此,一个简单的实现看起来像这样:
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);
}
这个检查确保u的所有者块不是t的“之前”,而t不是u的“之前”,所以t == u。
这个工作是否按照我的意图进行?从不同的shared_ptr创建的两个weak_ptr是否总是以这种方式比较为非相等? 或者我漏掉了什么吗?
编辑:我为什么要这样做? 我想要一个包含共享指针的容器,并且我想要分发对其中对象的引用。 我不能使用迭代器,因为它们可能无效。我可以分发(整数)ID,但这会导致唯一性问题,并且需要一个映射类型并且会复杂化搜索/插入/删除操作。 这个想法是使用std::set,并将指针本身(封装在一个包装类中)作为键,这样客户端可以使用weak_ptr来访问集合中的对象。
owner_before()
方法。对于我的使用情况,比较控制块是唯一明智的答案。 - vinipsmaker