什么是"权利"使用GUID作为std :: hash_map中的键的方法

时间:2014-06-09 04:52:44

标签: c++ visual-c++ boost stl

我想说

std::hash_map<GUID, int> foo;

我相信这样做我必须创建一个

bool operator < (const GUID &guid1, const GUID &guid2);
std::size_t hash_value(GUID const &b);

比较GUID的正确方法是什么? (memcmp?) - 生成哈希的正确方法是什么?

如果有人能够充实这两个功能,那就太棒了,我已经阅读了几十个帖子,这些帖子做了所有事情,但却给出了最后的线索: - )

6 个答案:

答案 0 :(得分:3)

来自documentation似乎:

typedef struct _GUID {
  DWORD Data1;
  WORD  Data2;
  WORD  Data3;
  BYTE  Data4[8];
} GUID;

可能有几种可能性

建立自己的

为了比较我会逐项推送

bool operator < (const GUID &guid1, const GUID &guid2) {
    if(guid1.Data1!=guid2.Data1) {
        return guid1.Data1 < guid2.Data1;
    }
    if(guid1.Data2!=guid2.Data2) {
        return guid1.Data2 < guid2.Data2;
    }
    if(guid1.Data3!=guid2.Data3) {
        return guid1.Data3 < guid2.Data3;
    }
    for(int i=0;i<8;i++) {
        if(guid1.Data4[i]!=guid2.Data4[i]) {
            return guid1.Data4[i] < guid2.Data4[i];
        }
    }
    return false;
}

对于散列...我会选择UuidHash函数(请注意,GUID是UUID的一种形式,如UUID definition所示)

返回字符串

使用StringFromCLSID从GUID获取字符串...一旦你有了字符串,就拥有了所有的操作符。

......这可能更贵。

答案 1 :(得分:3)

C ++库中没有std :: hash_map,stdext :: hash_map是一个过时的类(参见http://msdn.microsoft.com/en-us/en-en/library/0d462wfh.aspx)。

在std :: unordered_map中使用GUID:

#include <climits>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <unordered_map>

// Adopted from http://msdn.microsoft.com/en-us/library/windows/desktop/aa373931%28v=vs.85%29.aspx
typedef struct _GUID
{
    std::uint32_t Data1;
    std::uint16_t Data2;
    std::uint16_t Data3;
    std::uint8_t  Data4[8];
} GUID;

// Ensure it has 128 bits
static_assert(sizeof(_GUID) == 128/CHAR_BIT, "GUID");

// The compare operator is required by std::unordered_map
inline bool operator == (const GUID& a, const GUID& b) {
    return std::memcmp(&a, &b, sizeof(GUID)) == 0;
}

// A wrapper to create a GUID
inline GUID make_guid() {
    // Should return the value of some library function. 
    return GUID();
}

// Specialize std::hash
namespace std {
    template<> struct hash<GUID>
    {
        size_t operator()(const GUID& guid) const noexcept {
            const std::uint64_t* p = reinterpret_cast<const std::uint64_t*>(&guid);
            std::hash<std::uint64_t> hash;
            return hash(p[0]) ^ hash(p[1]);
        }
    };
}

// Usage
int main(void) {
    typedef std::unordered_map<GUID, int> map_type;
    map_type m;
    m.insert(map_type::value_type(make_guid(), 0));
    m.insert(map_type::value_type(make_guid(), 1));
    m.insert(map_type::value_type(make_guid(), 2));
}

答案 2 :(得分:3)

由于GUID只是POD,应该可以这样做:

namespace std
{
    template<> struct hash<GUID> : public std::_Bitwise_hash<GUID>
    {
    };
}

答案 3 :(得分:0)

你的GUID是什么类型的?考虑下面这段代码:

struct MyCompare : binary_function<SomeClass, SomeClass, bool>
{
    //constructor
    MyCompare(int (SomeClass::*p)() const, int (SomeClass::*p2)() const) : pointer(p), pointer2(p2) {}

    //comparison functor

    bool operator < (SomeClass const& left, SomeClass const& right)
    {
        if ((left.*pointer)() != (right.*pointer)())
        {
            return less<int>()((left.*pointer)(), (right.*pointer)());
        }
        else
        {
            return less<int>()((left.*pointer2)(), (right.*pointer2)());
        }
    }
    private:
    int (SomeClass::*pointer)() const;
    int (SomeClass::*pointer2)() const;
};

这是针对int类型的2个字段的复合键。排序第一个指针然后稳定排序第二个指针代码可能不会先尝试,但你明白了。

答案 4 :(得分:0)

对于Windows实现,我只需前往::UuidHash()。因此哈希函数将变为如此:

#include <rpc.h>

namespace std
{
    template<> struct hash<GUID>
    {
        size_t operator()(const GUID &Value) const
        {
            RPC_STATUS status = RPC_S_OK;
            return ::UuidHash(&const_cast<GUID&>(Value), &status);
        }
    };
}

说明:

  • 有趣的是:: UuidHash()只返回一个unsigned short,但这似乎足以散列GUID(为什么MS应该这样做呢?)。
  • 对于unordered_map / unordered_set no less(),运算符&lt;()等是必需的,只是一个算函数来计算哈希值。在内部比较哈希(找到包含的桶),而不是元素,这就是原因。
  • 如果需要进行比较以用于其他目的,请使用::UuidCompare()并将其隐藏在重载运算符中以获取语法糖(仍在等待太空船运营商实现......)。
  • 对于适合基于散列的STL容器的散列函数,不需要尽可能少的冲突。更重要的是快速哈希函数。如果你有可接受的小碰撞,它只会导致存储多个值的存储桶,但事情仍然可操作。那么:: UuidHash()返回的(可能是次优的?)unsigned short应该没问题。

答案 5 :(得分:-1)

首先,鉴于GUID是一般概念,您应该指定您正在谈论的GUID类。我会猜测它是this Microsoft Windows one,因为您标记了visual-c++ ....

快速搜索&#34;哈希GUID&#34;显示Microsoft提供了推荐的哈希,请参阅here。听起来它有严重的缺陷,描述here

如果由于某种原因您无法或不想使用它,请考虑数据成员:

typedef struct _GUID {
  DWORD Data1;
  WORD  Data2;
  WORD  Data3;
  BYTE  Data4[8];
} GUID;

这些非C ++ - 标准类型记录在案here。 你可以得到一个合理的32位散列,例如:

DWORD hash(const GUID& g)
{
    return std::hash(g.Data1 ^
                     ((g.Data2 << 16) | g.Data3) ^
                     *(DWORD*)(g.Data4) ^
                     *(DWORD*)(g.Data4 + 4);
}

要理解这一点 - 它有效地采用了这样的字段:

              [16 bits here]         [16 bits here]
      g.Data1=0101100101010010:1001010101001010
      g.Data2=1011110101001001 1011101010101010=g.Data3
g.Data4[0..3]=1010111010101010:0011101001000101
g.Data3[4..7]=1101111010100100:1010100011010110
         hash=1001010000010101:1011110101110001

从上到下,它将所有比特放在一起,所以如果上面的1比特奇数,则上面的最后一个哈希线为1。如果你想要一个更强的哈希,你可以通过例如比特左右移位一个贡献值。 3-16位,两者兼顾,或使用hash_combine()函数(google it!)。

对于哈希映射,您不需要operator<(您只需要一个用于排序的二叉树 - 即std::map),但确实需要能够比较等价...即operator==。您确实可以使用memcpy进行比较 - 给定字段大小和顺序,它清楚结构不需要填充,因此所有位对于比较都是有意义的。您必须使用memcmp,分别比较每个字符,或使用更多类型转换,因此我只使用单个memcmp运行....