简体   繁体   English

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

[英]Why no default hash for C++ POD structs?

I want to use a POD struct as a hash key in a map, eg 我想在地图中使用POD结构作为哈希键,例如

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

but I can't do this, since no hash function is auto-generatable for such structs. 但是我不能这样做,因为没有哈希函数可以自动生成这样的结构。

  • Why does the C++ standard not require a default hash for a POD struct? 为什么C ++标准不需要POD结构的默认哈希?
  • Why do compilers (specifically, GCC 4.x / 5.x) offer such a hash, even if the standard doesn't mandate one? 为什么编译器(特别是GCC 4.x / 5.x)提供这样的哈希值,即使标准没有强制要求呢?
  • How can I generate a hash function, using a template, in a portable way, for all of my POD structures (I'm willing to make semantic assumptions if necessary)? 如何使用模板以可移植的方式为我的所有POD结构生成哈希函数(如果需要,我愿意做出语义假设)?

As from the documentation , a possible implementation in your case would be: 文档中可以看出,您的案例可能是:

#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;
}

The compiler us not allowed to generate such a specialization because of the standard that does not define anything like that (as already mentioned in the comments). 编译器不允许生成这样的专门化,因为标准没有定义类似的东西(如评论中已经提到的)。

There is a method to generate hash for POD, like good old c style. 有一种为POD生成哈希的方法,就像旧的c风格一样。 Only for real POD with no any linked data on the outside of struct. 仅适用于结构外部没有任何链接数据的真实POD。 There is no checking of this requirements in code so use it only when you know and can guarantee this. 代码中没有检查此要求,因此只有在您知道并且可以保证这一点时才使用它。 All fields must be initialized (for example by default constructor like this A(), B() etc). 必须初始化所有字段(例如,默认构造函数,如此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: Data structure must be defined in data packing section with value of one byte or with pack attribute for prevent padding bytes. UPD:必须在数据打包部分中定义数据结构,其值为一个字节或具有pack属性以防止填充字节。

UPD: But I need to warn that replace deafult packing will make data loading/storing from/to memory for some fields little slowly, to prevent this need to arrange structure data fields with granularity that corresponding your (or most popular) architecture. UPD:但我需要警告,替换deafult包装会使某些字段的数据加载/存储到某些字段的速度很慢,以防止需要按照与您(或最常用)体系结构相对应的粒度排列结构数据字段。

I suggest that you can add by yourself additional unused fields not for using but for arrange fields in your data structure for best prformance of memory loading/storing. 我建议您可以自行添加其他未使用的字段,而不是用于数据结构中的排列字段,以获得最佳的内存加载/存储性能。 Example: 例:

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 is supported by, at least: #pragma pack至少支持:

More flexible way is to declare comparision class and use it as template param of std::unordered_map . 更灵活的方法是声明比较类并将其用作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;

You may want another Hash for same objects. 您可能需要另一个Hash用于相同的对象。 Flexibility appear with code like this: 灵活性出现在这样的代码中:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM