为什么没有C ++ POD结构的默认哈希?

时间:2016-03-06 11:39:33

标签: c++ templates hash struct unordered-map

我想在地图中使用POD结构作为哈希键,例如

struct A { int x; int y; };
std::unordered_map<A, int> my_map;

但是我不能这样做,因为没有哈希函数可以自动生成这样的结构。

  • 为什么C ++标准不需要POD结构的默认哈希?
  • 为什么编译器(特别是GCC 4.x / 5.x)提供这样的哈希,即使标准没有强制要求呢?
  • 如何使用模板以可移植的方式为我的所有POD结构生成哈希函数(如果需要,我愿意做出语义假设)?

3 个答案:

答案 0 :(得分:3)

documentation开始,您的案例中可能的实施方式是:

#include<functional>
#include<unordered_map>

struct A { int x; int y; };

namespace std
{
    template<> struct hash<A>
    {
        using argument_type = A;
        using result_type = std::size_t;
        result_type operator()(argument_type const& a) const
        {
            result_type const h1 ( std::hash<int>()(a.x) );
            result_type const h2 ( std::hash<int>()(a.y) );
            return h1 ^ (h2 << 1);
        }
    };
}

int main() {
    std::unordered_map<A, int> my_map;
}

编译器不允许生成这样的专门化,因为标准没有定义类似的东西(如评论中已经提到的那样)。

答案 1 :(得分:1)

有一种为POD生成哈希的方法,就像好的旧c样式一样。仅适用于结构外部没有任何链接数据的真实POD。代码中没有检查此要求,因此只有在您知道并且可以保证这一点时才使用它。必须初始化所有字段(例如,默认构造函数,如此A(),B()等)。

#pragma pack(push)  /* push current alignment to stack */
#pragma pack(1)     /* set alignment to 1 byte boundary */
    struct A { int x; int y; };
    struct B { int x; char ch[8] };
#pragma pack(pop)   /* restore original alignment from stack */

struct C { int x __attribute__((packed)); };


template<class T> class PodHash;

template<>
class PodHash<A> {
public:
    size_t operator()(const A &a) const
    {
        // it is possible to write hash func here char by char without using std::string
        const std::string str =
            std::string( reinterpret_cast<const std::string::value_type*>( &a ), sizeof(A) );
        return std::hash<std::string>()( str );
    }
};

std::unordered_map< A, int, PodHash<A> > m_mapMyMapA;
std::unordered_map< B, int, PodHash<B> > m_mapMyMapB;

UPD: 必须在数据打包部分中定义数据结构,其值为一个字节或使用pack属性以防止填充字节。

UPD: 但我需要警告的是,替换deafult包装会使某些字段的数据加载/存储到某些字段的速度很慢,以防止需要按照与您(或最常用)体系结构相对应的粒度排列结构数据字段。

我建议你可以自己添加其他未使用的字段,而不是用于数据结构中的排列字段,以便最好地记录内存加载/存储。例如:

struct A
{
    char x;           // 1 byte
    char padding1[3]; // 3 byte for the following 'int'
    int y;            // 4 bytes - largest structure member
    short z;          // 2 byte
    char padding2[2]; // 2 bytes to make total size of the structure 12 bytes
};
至少支持

#pragma pack

答案 2 :(得分:0)

更灵活的方法是声明比较类并将其用作std::unordered_map的模板参数。

struct A { int x; int y; };

emplate<class T> class MyHash;

template<>
class MyHash<A> {
public:
    size_t operator()(const A &a) const
    {
        result_type const h1 ( std::hash<int>()(a.x) );
        result_type const h2 ( std::hash<int>()(a.y) );
        return h1 ^ (h2 << 1);
    }
};

std::unordered_map<CString,CString,MyHash> m_mapMyMap;

您可能需要另一个Hash用于相同的对象。灵活性出现在这样的代码中:

std::unordered_map<CString,CString, *MyAnotherHas* > m_mapMyMap;