2014-10-16 368 views
1

我不理解正确的东西。我的印象是unordered_set不会允许基于它们的散列的重复元素。std :: unordered_set允许插入重复记录

我有一个结构,其中的std ::散列,这似乎允许重复的特殊化,虽然我已经手动检查它

AddEdge (const std::shared_ptr<Relation> relation, const std::shared_ptr<Concept> concept) 
{ 
    auto edge = std::make_shared<Edge>((Edge){ relation, concept }); 
    auto res = _edges.insert (edge); 
    return res.second; 
} 

一个重载函数不完全相同,但对于反转参数

这是边沿结构被散列:

namespace std 
{ 
    template<> struct hash<shared_ptr<Edge>> 
    { 
     size_t operator()(const shared_ptr<Edge> & ptr) const 
     { 
      size_t from = 0; 
      size_t to = 0; 

      if (auto node = std::dynamic_pointer_cast<Concept>(ptr->from)) 
       from = hash<shared_ptr<Concept>>()(node); 

      else if (auto node = std::dynamic_pointer_cast<Relation>(ptr->from)) 
       from = hash<shared_ptr<Relation>>()(node); 

      if (auto node = std::dynamic_pointer_cast<Concept>(ptr->to)) 
       to = hash<shared_ptr<Concept>>()(node); 

      else if (auto node = std::dynamic_pointer_cast<Relation>(ptr->to)) 
       to = hash<shared_ptr<Relation>>()(node); 

      return hash<size_t>()(from + to); 
     } 
    }; 
} 

而且在举行的容器:

std::unordered_set<std::shared_ptr<Edge>> _edges; 

当我这样做:

graph2->AddEdge(sea_node, is_node); 
graph2->AddEdge(is_node, blue_node); 

我得到:

Edge [sea,is] hash = 10017731961838884781 
Edge [is,blue] hash = 11178184051384786808 

我尝试第二次完全一样,我也得到相同的哈希值,但是,当我检查的边缘,我现在有4条边而不是2条。

我在做什么错?

编辑:类概念&关系有同样的散列函数:

namespace std 
{ 
    template<> struct hash<shared_ptr<Concept>> 
    { 
     size_t operator()(const shared_ptr<Concept> & ptr) const 
     { 
      return hash<string>()(ptr->asToken()->value()) + hash<int>()(ptr->TokenIndex()) + hash<string>()("Concept"); 
     } 
    }; 
} 

更interestignly,当我从加边我的输出,产生相同的哈希值,但它的重复边缘添加。

+0

请看[testcase](http://sscce.org)。 – 2014-10-16 23:17:23

+0

我哈希指针具有完全相同的哈希函数(请参阅编辑)。不幸的是,我不能创建一个测试用例,太多的类涉及一个小型自包含示例。 – 2014-10-16 23:21:07

+0

创建测试用例的关键是_abstract away_或_remove_这些类。除非您的问题依赖于高度本地化的因素,例如特殊硬件或疯狂的海森堡,否则构建一个小型自包含示例绝不是不可能的。你应该把它作为你自己调试的第一步之一,早在寻求帮助之前......我们甚至你怎么知道所有那些“太多的类”都不会导致问题? – 2014-10-16 23:23:01

回答

9

一个unordered_set将不允许重复的元素,基于其散列

否,unordered_set通过比较,这些值&匕首的未散列避免重复;
您的每个共享指针的“值”会因参考不同的对象而有所不同。

实际上,你可以通过提供自己的函数作为KeyEqual模板参数unordered_set改变这种行为:

template< 
    class Key, 
    class Hash = std::hash<Key>,   // <-- you've been looking at this 
    class KeyEqual = std::equal_to<Key>, // <-- instead of this 
    class Allocator = std::allocator<Key> 
> class unordered_set; 

&匕首;如果在unordered_set中只允许一个给定哈希值,那么(a)您将无法添加任何真正导致哈希冲突的值,并且(b)整个哈希冲突解决机制将变得完全不必要。

+0

我可能需要阅读几次才能消化它。感谢您的解释。所以,我应该实施std :: equal_to 还是没有意义? – 2014-10-16 23:23:04

+1

@Alex:我想这应该是'std :: equal_to >',但我真的认为你的代码在阅读和使用所有这些透明的解引用时会非常混乱。我想你就是你。至少我会将'Hash'和'KeyEqual'函数作为模板参数传递给您的集合类型,而不是将这些实现一般用于您的所有代码。 – 2014-10-16 23:32:07

+1

@Alex请注意,'equal_to'必须与'hash'一致地实现,也就是说,如果'equal_to(ptr1,ptr2)'然后'hash(ptr1)== hash(ptr2)' – 2014-10-17 00:42:50