2014-12-04 70 views
0

我試圖在Visual C++ 16.0各種實現方式(一個附帶的Visual Studio 2010),我讓所有類型的bug與std::unordered_mapunordered_map不起作用

例如

CKey key = pszName; 
auto it = m_Records.find(key); 
if (it != m_Records.end()) 
{ 
    // we replace existing item (so delete old) 
    delete it->second; 
    it->second = pRecord; 
} 
else 
{ 
    const size_t sz = m_Records.size(); 
    m_Records.insert(std::make_pair(key, pRecord)); 
    const size_t sz2 = m_Records.size(); 
    assert((sz + 1) == sz2); // this assertion fails! wtf! 
} 

m_Records是一個std :: unordered_map實例。所以我切換到boost::unordered_map 1.48。現在,這確實有用,但我在其他地方有另一個問題雖然上面的代碼相同,但是相同的密鑰不斷插入兩次或更多次。我的地圖如何管理最簡單的事情,並且每個密鑰只保留一個條目?

我有三倍檢查哈希函數和比較函數。我不相信他們應該責怪在這裏。

我在做什麼錯?

m_Records的類型是boost::unordered_map<CKey, CRecord*>std::unordered_map<CKey, CRecord*>

CKey被定義爲這樣:

struct CKey 
{ 
    const wchar_t* m_Str; 
    int m_Len; 

    CKey(const wchar_t* s) 
    : m_Str(s) 
    , m_Len(s ? (int)wcslen(s) : 0) 
    { 
    } 

    size_t hash() const 
    { 
    if (this->m_Len > 0) 
    { 
     char temp[16]; 
     memset(temp, 0, sizeof(temp)); 
     MurmurHash3_x64_128(this->m_Str, (int)sizeof(wchar_t) * this->m_Len, 0, temp); 
     size_t hash = *(size_t*)temp; 
     return hash; 
    } 
    return 0; 
    } 

    bool operator==(const CKey& other) const 
    { 
    if ((this->m_Len > 0) & (this->m_Len == other.m_Len)) 
    { 
     return (wcscmp(this->m_Str, other.m_Str) == 0); 
    } 
    // otherwise, they are only equal if they are both empty 
    return (this->m_Len == 0) & (other.m_Len == 0); 
    } 
}; 

namespace boost 
{ 
template <> 
struct hash<CKey> 
{ 
    size_t operator()(const CKey& k) const 
    { 
    return k.hash(); 
    } 
}; 
} 

namespace std 
{ 
template <> 
struct equal_to<CKey> 
{ 
    bool operator()(const CKey& x, const CKey& y) const 
    { 
    return (x == y); 
    } 
}; 
} 
+1

調試器在斷言失敗時說什麼是關鍵有趣的呢?密鑰可能在地圖中發生變異? (也就是說,它們是否包含指向其他人指向的資源的指針?)您可以生成一個MCVE嗎?什麼版本的'boost'? – Yakk 2014-12-04 08:15:32

+0

它全是單線程的,沒有併發性,所以在插入或任何映射操作發生時,我看不出有什麼變化。 – 2014-12-04 08:16:42

+2

不是,鑰匙在地圖中的全部時間,而不僅僅是當您調用地圖方法時。意外地在地圖(有序或無序)中變換鍵是常見的'哎呀',導致UB和意想不到的奇怪。密鑰在存儲時必須是不可變的哈希和比較。沒有更多的細節,只是在黑暗中刺傷。 – Yakk 2014-12-04 08:21:37

回答

3

原來的問題是一個簡單的共享存儲器的問題。我在不知不覺中沒有考慮到我用來插入項目的內存來自一個臨時變量。儘管所有內容都是堆內存,但實際的鍵值(不是散列或桶位置)從入口變爲入口。這又導致了上述不一致和不合邏輯的操作。

人們已經認識到,當問題的性質不合邏輯時,問題的原因很可能是相似的。我只是將CKey中的const char* m_Str成員聲明更改爲std::wstring m_Str,並且這樣做了。

該修復使CKey結構顯着變小,這很好。用我原來的實現代替這個工作很好。

struct CKey 
{ 
    std::wstring m_Str; 

    CKey(const wchar_t* s) 
    : m_Str(s) 
    { 
    } 

    size_t hash() const 
    { 
    if (!this->m_Str.empty()) 
    { 
     char temp[16]; 
     memset(temp, 0, sizeof(temp)); 
     MurmurHash3_x64_128(this->m_Str.c_str(), (int)sizeof(wchar_t) * (int)this->m_Str.size(), 0, temp); 
     size_t hash = *(size_t*)temp; 
     return hash; 
    } 
    return 0; 
    } 

    bool operator==(const CKey& other) const 
    { 
    return this->m_Str.compare(other.m_Str) == 0; 
    } 
}; 
+0

如果您包含固定代碼片段的外觀,它會非常有幫助。 – 2014-12-05 10:19:17

+0

如果你必須堅持。 – 2014-12-05 11:10:43