2014-12-04 71 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