为什么我不能将对象存储在unordered_set中?

时间:2016-11-22 21:00:01

标签: c++ c++11 unordered-set

我理解一个集合是有序的,因此在不重载<运算符的情况下添加一个对象并不能说明哪个对象更小以保持容器的排序。但是,我不明白为什么unordered_set无法做到这一点。

如果我尝试这样的话:

#include <iostream>
#include <string
#include <unordered_set>

struct someType{
    string name;
    int code;
};

int main(){
    std::unordered_set <someType> myset;
    myset.insert({"aaa",123});
    myset.insert({"bbb",321});
    myset.insert({"ccc",213});
    return 0;
}

我收到了一些错误:

  

c:\ qt \ qt5.1.0 \ tools \ mingw48_32 \ lib \ gcc \ i686-w64-mingw32 \ 4.8.0 \ include \ c ++ \ bits \ hashtable_policy.h:1070:错误:无效使用不完整类型& #39; struct std :: hash&#39;

     

c:\ qt \ qt5.1.0 \ tools \ mingw48_32 \ lib \ gcc \ i686-w64-mingw32 \ 4.8.0 \ include \ c ++ \ bits \ functional_hash.h:58:错误:声明&#39; struct std :: hash&#39;

     

错误:没有匹配函数来调用&#39; std :: unordered_set :: unordered_set()&#39;

     

c:\ qt \ qt5.1.0 \ tools \ mingw48_32 \ lib \ gcc \ i686-w64-mingw32 \ 4.8.0 \ include \ c ++ \ bits \ hashtable_policy.h:1103:错误:无法匹配调用& #39;(const std :: hash)(const someType&amp;)&#39;

     

c:\ qt \ qt5.1.0 \ tools \ mingw48_32 \ lib \ gcc \ i686-w64-mingw32 \ 4.8.0 \ include \ c ++ \ bits \ stl_function.h:208:错误:不匹配&#39 ;运算符==&#39; (操作数类型是&#39; const someType&#39;和&#39; const someType&#39;)

为什么会这样,我该如何解决?

2 个答案:

答案 0 :(得分:9)

要在unordered_set或unordered_map中使用type,您需要为您的类型提供散列函数。对于常见类型,例如intstd::string - 散列函数由标准库提供。对于您的类型,您可以重载标准std::hash,如下所示:

namespace std {
    template <> struct hash<someType> {
        size_t operator()(const someType & x) const {
            std::hash<std::string> h;
            return h(x.name);
            // or simply return x.code
            // or do something more interesting,
            // like xor'ing hashes from both members of struct
        }
    };
}

另一种方法是为您自己的类型提供重载operator()并将其作为哈希模板参数放在unordered_set中,如下所示:

struct someTypeHasher {
    size_t operator()(const someType& x) const {
        return x.code;
    }
};
std::unordered_set<someType, someTypeHasher> myset;

关于基于散列的容器的理论的良好解读是here

此外,请不要忘记,您需要为operator==重载someType,如果没有它,它也将无效。

答案 1 :(得分:1)

Starl1ght 给出的answer中所述,您需要为someType提供哈希函数。但是,我将通过该哈希函数组合您的类的所有成员。否则,例如,如果相同的name经常发生但具有不同的code值,则可能会发生很多冲突。要创建哈希函数,可以使用Boost,但也可以handcraft

Starl1ght 还提到您需要为operator==重载someType, 但是您也可以定义一个单独的比较函数,并将其提供给unordered_set。此外,您可以使用lambda expressions而不是定义哈希和比较函数。如果将所有内容放在一起,则您的代码可以编写如下:

auto hash = [](const someType& st){
    return std::hash<std::string>()(st.name) * 31 + std::hash<int>()(st.code);
};
auto equal = [](const someType& st1, const someType& st2){
    return st1.name == st2.name && st1.code == st2.code;
};
std::unordered_set<someType, decltype(hash), decltype(equal)> myset(8, hash, equal);

Code on Ideone