【问题标题】:How can I use an unordered_set with a custom struct?如何将 unordered_set 与自定义结构一起使用?
【发布时间】:2018-06-16 12:56:43
【问题描述】:

我想使用 unordered_set 和自定义 struct。在我的例子中,自定义 struct 代表欧几里得平面中的二维点。我知道应该定义一个哈希函数和比较器运算符,我已经这样做了,您可以在下面的代码中看到:

struct Point {
    int X;
    int Y;

    Point() : X(0), Y(0) {};
    Point(const int& x, const int& y) : X(x), Y(y) {};
    Point(const IPoint& other){
        X = other.X;
        Y = other.Y;
    };

    Point& operator=(const Point& other) {
        X = other.X;
        Y = other.Y;
        return *this;
    };

    bool operator==(const Point& other) {
        if (X == other.X && Y == other.Y)
            return true;
        return false;
    };

    bool operator<(const Point& other) {
        if (X < other.X )
            return true;
        else if (X == other.X && Y == other.Y)
            return true;

        return false;
    };

    size_t operator()(const Point& pointToHash) const {
        size_t hash = pointToHash.X + 10 * pointToHash.Y;
        return hash;
    };
};

但是,如果我按如下方式定义集合,则会出现以下错误:

unordered_set<Point> mySet;

错误 C2280 'std::hash<_kty>::hash(const std::hash<_kty> &)': 试图引用已删除的函数

我错过了什么?

【问题讨论】:

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


    【解决方案1】:

    std::unordered_set 的第二个模板参数是用于散列的类型。并且在您的情况下默认为std::hash&lt;Point&gt;,它不存在。因此,如果哈希器类型相同,您可以使用std::unordered_set&lt;Point,Point&gt;

    或者,如果您不想指定散列器,则为Point 定义一个std::hash 的特化,然后去掉成员函数并在你的特化的operator() 的主体中实现散列,或者调用来自 std::hash 特化的成员函数。

    #include <unordered_set>
    
    struct Point {
        int X;
        int Y;
    
        Point() : X(0), Y(0) {};
        Point(const int& x, const int& y) : X(x), Y(y) {};
        Point(const Point& other){
            X = other.X;
            Y = other.Y;
        };
    
        Point& operator=(const Point& other) {
            X = other.X;
            Y = other.Y;
            return *this;
        };
    
        bool operator==(const Point& other) const {
            if (X == other.X && Y == other.Y)
                return true;
            return false;
        };
    
        bool operator<(const Point& other) {
            if (X < other.X )
                return true;
            else if (X == other.X && Y == other.Y)
                return true;
    
            return false;
        };
    
        // this could be moved in to std::hash<Point>::operator()
        size_t operator()(const Point& pointToHash) const noexcept {
            size_t hash = pointToHash.X + 10 * pointToHash.Y;
            return hash;
        };
    
    };
    
    namespace std {
        template<> struct hash<Point>
        {
            std::size_t operator()(const Point& p) const noexcept
            {
                return p(p);
            }
        };
    }
    
    
    int main()
    {
        // no need to specify the hasher if std::hash<Point> exists
        std::unordered_set<Point> p;
        return 0;
    }
    

    Demo

    【讨论】:

    • 谢谢@rawatson!我决定使用第一个选项 - 指定散列函数。对所有新的无序关联结构来说都很新,我肯定有很多东西要学。
    【解决方案2】:

    虽然上述解决方案可以让您编译代码,但请避免使用该哈希函数作为点。有一个由b 参数化的一维子空间,y = -x/10 + b 线上的所有点都将具有相同的哈希值。最好使用 64 位散列,其中前 32 位是 x 坐标,低 32 位是 y 坐标(例如)。看起来像

    uint64_t hash(Point const & p) const noexcept
    {
        return ((uint64_t)p.X)<<32 | (uint64_t)p.Y;
    }
    

    【讨论】:

      【解决方案3】:

      我想通过提供更多提示来扩展 rmawatson's answer

      1. 对于您的struct,您无需定义operator=Point(const Point&amp; other),因为您(重新)实现了默认行为。
      2. 您可以通过删除if 子句来简化operator==,如下所示:

        bool operator==(const Point& other) { return X == other.X && Y == other.Y; };
        
      3. 您的operator&lt; 中有一个错误:在else if 子句中,如果两个点相等,您将返回true。这违反了strict weak ordering 的要求。因此,我建议改用以下代码:

        bool operator<(const Point& other) { return X < other.X || (X == other.X && Y < other.Y); };
        

      此外,由于C++11,您可以使用lambda expressions 来代替定义散列和比较函数。这样,您不需要为您的struct 指定任何运算符,如果您不需要它们。将所有内容放在一起,您的代码可以编写如下:

      struct Point {
          int X, Y;
      
          Point() : X(0), Y(0) {};
          Point(const int x, const int y) : X(x), Y(y) {};
      };
      
      int main() {
          auto hash = [](const Point& p) { return p.X + 10 * p.Y; };
          auto equal = [](const Point& p1, const Point& p2) { return p1.X == p2.X && p1.Y == p2.Y; };
          std::unordered_set<Point, decltype(hash), decltype(equal)> mySet(8, hash, equal);
      
          return 0;
      }
      

      但是,正如 CJ13's answer 中所解释的,您的哈希函数可能不是最好的。 handcraft a hash function 的另一种方式如下:

      auto hash = [](const Point& p) { return std::hash<int>()(p.X) * 31 + std::hash<int>()(p.Y); };
      

      可以在here 找到更通用的哈希解决方案。

      Code on Ideone

      【讨论】:

        猜你喜欢
        • 2012-04-01
        • 1970-01-01
        • 2020-08-20
        • 1970-01-01
        • 1970-01-01
        • 2023-03-11
        • 2018-09-29
        • 2016-07-03
        相关资源
        最近更新 更多