如何使用stdext :: hash_map,其中键是自定义对象?

时间:2009-06-29 17:45:53

标签: c++ stl hashmap

使用STL C ++ hash_map ...

class MyKeyObject
{
    std::string str1;
    std::string str2;

    bool operator==(...) { this.str1 == that.str1 ... }
};

class MyData
{
    std::string data1;
    int data2;
    std::string etcetc;
};
像这样......

MyKeyObject a = MyKeyObject(...);
MyData b = MyData(...);

stdext::hash_map <MyKeyObject, MyData> _myDataHashMap;
_myDataHashMap[ a ] = b;

我得到了一大堆错误。这是前三个......

  

错误1错误C2784:'布尔   std :: operator&lt;(const   的std :: _树&LT; _Traits&GT; &安培;,const的   的std :: _树&LT; _Traits&GT; &amp;)':不能   推导'const。的模板参数   的std :: _树&LT; _Traits&GT; &安培;”来自'const   MyKeyObject'c:\ program files \ microsoft   视觉工作室   8 \ vc \ include \ functional 143

     

错误2错误C2784:'布尔   std :: operator&lt;(const   的std :: basic_string的&LT; _Elem,_Traits,_Alloc&GT;   &amp;,const _Elem *)':无法演绎   'const的模板参数   的std :: basic_string的&LT; _Elem,_Traits,_Alloc&GT;   &安培;”来自'const   任务:: MyKeyObject'c:\ program   files \ microsoft visual studio   8 \ vc \ include \ functional 143

     

错误3错误C2784:'布尔   std :: operator&lt;(const _Elem *,const   的std :: basic_string的&LT; _Elem,_Traits,_Alloc&GT;   &amp;)':无法推断模板   来自'const _Elem *'的参数   'const MyDataObject'c:\ program   files \ microsoft visual studio   8 \ vc \ include \ functional 143

     

...

如果我将键设置为像int一样简单的东西,那么一切都很好。

我做错了什么?!也许我需要用模板做些什么?

是否有更好(更快?)的方式使用像这样的自定义密钥对象访问数据?

5 个答案:

答案 0 :(得分:3)

要使用哈希表,您需要指定哈希函数。您需要创建一个函数对象,该对象表示一个带有MyKeyObject对象并返回size_t的函数。然后在最初的大小之后将仿函数作为第二个参数传递:

hash_map <MyKeyObject, MyData> _myDataHashMap(initial_size, YourHashFunctor());

或者,您可以将哈希函数编写为类型的hash<T>仿函数的模板特化;这样你就不需要传递自定义哈希函数了。

我不知道为什么你会特意得到这些错误。也许它试图将您的对象用作哈希代码或其他什么?在任何情况下,如果没有哈希函数,它都不应该工作。散列函数是为整数类型和字符串预定义的。

答案 1 :(得分:2)

&LT;已删除链接&gt; 清楚地解释了如何使用hash_map并创建自己的哈希函数。

(编辑:已删除链接。现在指向垃圾邮件页面)

答案 2 :(得分:2)

尝试以下操作,在VS 2005中为我工作。这是stdext命名空间中VS2005内置hash_map类型以及boost unordered_map(首选)的解决方案。删除任何不使用的内容。

#include <boost/unordered_map.hpp>
#include <hash_map>

class HashKey
{
public:
    HashKey(const std::string& key)
    {
        _key=key;
    }
    HashKey(const char* key)
    {
        _key=key;
    }

    // for boost and stdext
    size_t hash() const
    {
        // your own hash function here
        size_t h = 0;
        std::string::const_iterator p, p_end;
        for(p = _key.begin(), p_end = _key.end(); p != p_end; ++p)
        {
            h = 31 * h + (*p);
        }
        return h;
    }
    // for boost
    bool operator==(const HashKey& other) const
    {
        return _key == other._key;
    }

    std::string _key;
};

// for boost
namespace boost
{
    template<>
    class hash<HashKey>
    {
    public :
        std::size_t operator()(const HashKey &mc) const
        {
            return mc.hash();
        }
    };
}

// for stdext
namespace stdext
{
    template<>
    class hash_compare<HashKey>
    {
    public :
        static const size_t bucket_size = 4;
        static const size_t min_buckets = 8;

        size_t operator()(const HashKey &mc) const
        {
            return mc.hash();
        }

        bool operator()(const HashKey &mc1, const HashKey &mc2) const
        {
            return (mc1._key < mc2._key);
        }
    };
}

int _tmain(int argc, _TCHAR* argv[])
{
    {
        stdext::hash_map<HashKey, int> test;
        test["one"] = 1;
        test["two"] = 2;
    }

    {
        boost::unordered_map<HashKey, int> test(8); // optional default initial bucket count 8
        test["one"] = 1;
        test["two"] = 2;
    }

    return 0;
}

答案 3 :(得分:0)

我用它来映射顶点数据的结构。

#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <boost/unordered_map.hpp>



struct VERTEX
{
float x,y,z;
};

typedef boost::unordered_map<std::string, unsigned int> map;

int main()
{
VERTEX v1,v2,v3;

v1.x = 5.0; v1.y = 2.0; v1.z = 2.33333336;
v2.x = 5.0; v2.y = 2.0; v2.z = 2.32333336;
v3.x = 5.0; v3.y = 2.0; v3.z = 2.33333336;

unsigned int vertexSize = sizeof( VERTEX );
char * v1c = new char[vertexSize];
char * v2c = new char[vertexSize];
char * v3c = new char[vertexSize];

memcpy( v1c, &v1, vertexSize );memcpy( v2c, &v2, vertexSize );memcpy( v3c, &v3, vertexSize );
map mymap;

std::string aaa( v1c, vertexSize );
std::string bbb( v2c, vertexSize );
std::string ccc( v3c, vertexSize );

mymap[ aaa ] = 1;
mymap[ bbb ] = 2;

unsigned int a = mymap[ aaa ];
unsigned int b = mymap[ bbb ];
unsigned int c = mymap[ ccc ];


return 0;
}

这只是一个小例子,我如何使用自定义类型。我只是将struct的部分内存复制到char *然后我用第二个param创建字符串,这是大小,大小很重要,因为内存数据可以包含空字符。我不需要任何额外的比较,散列函数......

答案 4 :(得分:0)

在尝试找到相同的答案时,我遇到了这个非常古老的问题,并发现现有的答案并不真正有用。如果我们想要一个哈希映射,现在我们使用unordered_map,并且让你的MyKeyObject类可用作hash_map中的键的最佳方法是为类定义一个哈希函数,并告诉它标准库将此哈希函数用于地图。这意味着我们可以在不提供哈希函数的情况下实例化地图模板。

C ++中的wikipedia page&#39;无序关联容器&#39;提供了一个易于理解的示例,我把它简化了一下并将其应用到您的案例中。首先,我们将一个简单的哈希函数定义为成员方法:

#include <functional>
class MyKeyObject {
private:
    std::string str1;
    std::string str2;

public:
    inline size_t hash() const {
        return std::hash<std::string>()(str1) ^ std::hash<std::string>()(str2);
    }

    inline bool operator==(const MyKeyObject& other) const {
        return str1 == other.str1 && str2 == other.str2;
    }
};

为了制作哈希函数,我们将所有包含对象的哈希值合并在一起。这是使用std::hash完成的,MyKeyObject是一个必须用子类型实例化的模板。请注意,我们不能将其用作unordered_map的第三个模板参数。另请注意const-equals运算符。

现在我们必须告诉标准库这是用于namespace std { template <> class hash<MyKeyObject> { public: size_t operator()(const MyKeyObject &aMyKeyObject) const { return aMyKeyObject.hash(); } }; } 值的哈希函数:

std::hash

这为MyKeyObject模板类添加了模板特化,为MyKeyObject类提供了哈希运算符。这个例子没有维基百科页面直接在这里定义哈希,而不是调用作为对象成员的哈希函数 - 但是如果哈希函数必须访问私有成员,那么它将无法工作。

现在,您应该可以在unordered_map中使用 std::unordered_map<MyKeyObject, MyData> _myDataHashMap; ,如此:

{{1}}

(使用clang / xcode测试)