2014-10-16 146 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