我有这样的一套:set<weak_ptr<Node>, owner_less<weak_ptr<Node> > > setName;
工作正常。但我想将它改为无序集。但是,当我这样做时,我会得到大约六页的错误。任何想法如何做到这一点?
在查看了错误消息的所有页面之后,我找到了可能有帮助的行。
/usr/include/c++/4.7/bits/functional_hash.h:60:7: error: static assertion failed: std::hash is not specialized for this type
/usr/include/c++/4.7/bits/stl_function.h: In instantiation of ‘bool std::equal_to<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = std::weak_ptr<Node>]’:
答案 0 :(得分:11)
简短而不幸的答案是,虽然shared_ptr<>
可以安全地用作无序集或地图中的关键字,但weak_ptr<>
不能也不能。没有多少诡计可以使它安全。
这是因为weak_ptr
的接口不公开对共享控制对象的访问,这是在有序集或映射中使用时owner_before()
进行比较的基础。
虽然锁定指针然后散列shared_ptr
似乎是合理的,但事实并非如此。如果最后一个shared_ptr
超出范围,则哈希值将更改,这将导致下次迭代set或map时出现未定义的行为。在您的代码在客户面前投入生产之前,您很可能不会注意到,偶尔会出现意外和无法解释的功能,但您的单元测试仍然会完美无缺,让您错误地认为您的测试覆盖率良好,您的代码是可靠的,应该归咎于用户,硬件或网络。
因此,总而言之,如果您要使用weak_pt
来构建非拥有对象缓存(它们非常出色),则需要使用{{1}并且遭受微乎其微的性能损失(尽管实际上这会因保护集合的std::set<weak_ptr>
导致的性能损失而相形见绌。)
如果你真的想使用mutex
作为无序密钥,你必须自己编写(提示:使用共享控制块的地址作为哈希函数的基础)。
答案 1 :(得分:9)
我不认为建议的哈希函数是正确的。如果对象的所有共享指针都消失,那么weak_ptr<X>::lock()
将返回空的shared_ptr,其哈希值可能为零。因此,哈希函数可以在整个时间内返回不同的值。
我认为这里的正确解决方案是使用
boost::unordered_map<X*, boost::weak_ptr<X>>
。类型X*
可以很容易地用作哈希映射和weak_ptr<X>
的键,因为该值使您有机会找出引用的对象是否仍然存在。
要将值存储到此哈希,您可以使用以下内容:
if (boost::shared_ptr<X> p = wp.lock()) {
// weak_ptr is still valid
ptrs.insert(std::make_pair(p.get(), p));
}
答案 2 :(得分:3)
由于unordered_sets
是基于散列的,因此您必须为std :: weak_ptr数据类型提供散列function object。
如果你看一下unordered_set模板参数
template<class Key,
class Hash = std::hash<Key>,
class Pred = std::equal_to<Key>,
class Alloc = std::allocator<Key> >
class unordered_set;
你会注意到std :: unordered_set为你提供了一个默认的std :: hash&lt;&gt;模板参数。但由于std :: hash仅为specific set数据类型提供特化,因此您可能必须提供自己的数据类型。
您引用的错误消息告诉您,没有std :: hash&lt;&gt; std :: weak_ptr&lt;&gt;的专业化存在,所以你必须为此提供自己的散列函数:
template<typename T>
struct MyWeakPtrHash : public std::unary_function<std::weak_ptr<T>, size_t> {
size_t operator()(const std::weak_ptr<T>& wp)
{
// Example hash. Beware: As zneak remarked in the comments* to this post,
// it is very possible that this may lead to undefined behaviour
// since the hash of a key is assumed to be constant, but will change
// when the weak_ptr expires
auto sp = wp.lock();
return std::hash<decltype(sp)>()(sp);
}
};
修改强> 您还需要提供相等函数,因为没有提供weak_ptr的std :: equal_to。 从"Equality-compare std::weak_ptr" on Stackoverflow:
采取可行的方法template<typename T>
struct MyWeakPtrEqual : public std::unary_function<std::weak_ptr<T>, bool> {
bool operator()(const std::weak_ptr<T>& left, const std::weak_ptr<T>& right)
{
return !left.owner_before(right) && !right.owner_before(left);
}
};
所有这些结果给了我们以下内容:
std::unordered_set<std::weak_ptr<T>,
MyWeakPtrHash<T>,
MyWeakPtrEqual<T>> wpSet;
答案 3 :(得分:-1)
感谢lx,现在构建了以下代码。为了使用gcc 4.7.2构建它我不得不再添加两个'const',所以现在代码下面应该是将std :: weak_ptr放入std :: unordered_set的完整工作示例。
#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_set>
#include <functional>
using namespace std;
template<typename T>
struct MyWeakPtrHash : public std::unary_function<std::weak_ptr<T>, size_t> {
size_t operator()(const std::weak_ptr<T>& wp) const
{
auto sp = wp.lock();
return std::hash<decltype(sp)>()(sp);
}
};
template<typename T>
struct MyWeakPtrEqual : public std::unary_function<std::weak_ptr<T>, bool> {
bool operator()(const std::weak_ptr<T>& left, const std::weak_ptr<T>& right) const
{
return !left.owner_before(right) && !right.owner_before(left);
}
};
int main() {
unordered_set<std::weak_ptr<string>, MyWeakPtrHash<string>,
MyWeakPtrEqual<string> > stringset;
shared_ptr<string> shptr(new string("Hi there") );
stringset.insert( shptr );
cout << stringset.size() << endl;
}