如何在C ++中创建一组无序的整数对?

时间:2013-03-01 15:11:24

标签: c++ std-pair unordered-set

以下程序不会编译一组无序的整数对,但它会对整数进行编译。可以unordered_set及其成员函数用于用户定义的类型,我该如何定义它?

#include <unordered_set>
...

class A{
...
private: 
    std::unordered_set< std::pair<int, int> > u_edge_;
};

编译错误:

  

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

8 个答案:

答案 0 :(得分:35)

没有标准方法来计算对上的哈希值。将此定义添加到您的文件中:

struct pair_hash {
    inline std::size_t operator()(const std::pair<int,int> & v) const {
        return v.first*31+v.second;
    }
};

现在您可以像这样使用它:

std::unordered_set< std::pair<int, int>,  pair_hash> u_edge_;

这很有效,因为pair<T1,T2>定义了相等性。对于不提供测试相等性的方法的自定义类,您可能需要提供单独的函数来测试两个实例是否彼此相等。

当然这个解决方案仅限于一对两个整数。这里有a link to an answer,可帮助您定义为多个对象制作哈希的更通用方法。

答案 1 :(得分:22)

您的代码在VS2010 SP1(VC10)上编译,但无法使用GCC g ++ 4.7.2进行编译。

但是,您可能需要考虑Boost.Functional中的boost::hash来散列std::pair(此外,您的代码也会使用g ++编译)。

#include <unordered_set>
#include <boost/functional/hash.hpp>

class A
{
private: 
    std::unordered_set< 
        std::pair<int, int>, 
        boost::hash< std::pair<int, int> > 
    > u_edge_;
};

答案 2 :(得分:8)

问题在于std::unordered_set正在使用std::hash模板来计算其条目的哈希值,并且对词组没有std::hash专门化。所以你必须做两件事:

  1. 决定您要使用的哈希函数。
  2. 使用该功能为您的密钥类型(std::hash)专门化std::pair<int, int>
  3. 这是一个简单的例子:

    #include <unordered_set>
    
    namespace std {
    template <> struct hash<std::pair<int, int>> {
        inline size_t operator()(const std::pair<int, int> &v) const {
            std::hash<int> int_hasher;
            return int_hasher(v.first) ^ int_hasher(v.second);
        }
    };
    
    }
    
    int main()
    {
        std::unordered_set< std::pair<int, int> > edge;
    }
    

答案 3 :(得分:3)

您需要为std::hash<>提供适用于std::pair<int, int>的专精。以下是如何定义专业化的一个非常简单的示例:

#include <utility>
#include <unordered_set>

namespace std
{
    template<>
    struct hash<std::pair<int, int>>
    {
        size_t operator () (std::pair<int, int> const& p)
        {
            // A bad example of computing the hash, 
            // rather replace with something more clever
            return (std::hash<int>()(p.first) + std::hash<int>()(p.second));
        }
    };
}

class A
{
private:
    // This won't give you problems anymore
    std::unordered_set< std::pair<int, int> > u_edge_;
};

答案 4 :(得分:1)

您缺少std::pair<int, int>>的哈希函数。例如,

struct bad_hash
{
  std::size_t operator()(const std::pair<int,int>& p) const
  {
    return 42;
  }
};

....

std::unordered_set< std::pair<int, int>, bad_hash> u_edge_;

您还可以为std::hash<T>专门设置std::hash<std::pair<int,int>>,在这种情况下,您可以省略第二个模板参数。

答案 5 :(得分:1)

这里的其他答案都建议构建一个以某种方式组合两个整数的哈希函数。

这将起作用,但是会产生非唯一的哈希。尽管这对于您使用unordered_set来说是很好的,但是对于某些应用程序来说可能是不可接受的。就您而言,如果您碰巧选择了错误的哈希函数,则可能会导致许多不必要的冲突。

但是您可以产生独特的哈希值!

int通常为4个字节。您可以通过使用int32_t使其明确。

哈希的数据类型为std::size_t。在大多数计算机上,这是8个字节。您可以在编译时进行检查。

由于一对由两种int32_t类型组成,因此您可以将两个数字都放在std::size_t中以进行唯一的哈希处理。

看起来像这样(我无法立即回想起如何强制编译器将带符号的值视为未带符号的位处理,因此我为uint32_t编写了以下内容):

#include <cassert>
#include <cstdint>
#include <unordered_set>
#include <utility>


struct IntPairHash {
  std::size_t operator()(const std::pair<uint32_t, uint32_t> &p) const {
    assert(sizeof(std::size_t)>=8);  //Ensure that std::size_t, the type of the hash, is large enough
    //Shift first integer over to make room for the second integer. The two are
    //then packed side by side.
    return (((uint64_t)p.first)<<32) | ((uint64_t)p.second);
  }
};

int main(){
  std::unordered_set< std::pair<uint32_t, uint32_t>, IntPairHash> uset;
  uset.emplace(10,20);
  uset.emplace(20,30);
  uset.emplace(10,20);
  assert(uset.size()==2);
}

答案 6 :(得分:0)

正如该问题的其他大多数答案中已经提到的那样,您需要为std::pair<int, int>提供一个哈希函数。但是,由于C++11,您还可以使用lambda expression而不是定义哈希函数。以下代码以solution given by dasblinkenlight为基础:

auto hash = [](const std::pair<int, int>& p){ return p.first * 31 + p.second; };
std::unordered_set<std::pair<int, int>, decltype(hash)> u_edge_(8, hash);

Code on Ideone

我想重复一下dasblinkenlight的免责声明:此解决方案仅限于两个整数对。 This answer提供了一种更通用的解决方案。

答案 7 :(得分:0)

好的,这是一个保证无冲突的简单解决方案。只需将问题简化为现有的解决方案,即可将int对转换为string,如下所示:

 auto stringify = [](const pair<int, int>& p, string sep = "-")-> string{
    return to_string(p.first) + sep + to_string(p.second);
 }

 unordered_set<string> myset;
 myset.insert(stringify(make_pair(1, 2)));
 myset.insert(stringify(make_pair(3, 4)));
 myset.insert(stringify(make_pair(5, 6)));

享受!