带有自定义结构的Unordered_set

时间:2018-06-16 12:56:43

标签: c++11 unordered-set

我一直想将unordered_set与自定义结构一起使用,在我的例子中,自定义结构表示欧几里得平面中的2D点。 现在,我知道应该定义一个哈希函数和比较器运算符,见下文:

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&lt; _Kty&gt; :: hash(const std :: hash&lt; _Kty&gt;&amp;)':   试图引用已删除的功能

在这里显然遗漏了什么但是????

3 个答案:

答案 0 :(得分:4)

std :: unordered_set的第二个模板参数是用于散列的类型。并且在您的情况下默认为std::hash<Point>,这不存在。因此,如果hasher是相同的类型,则可以使用std::unordered_set<Point,Point>

或者,如果您不想指定hasher,请为std::hash定义Point的特化,然后摆脱成员函数并在专业化的{{1}中实现散列或者从std :: hash specialization调用成员函数。

operator()

Demo

答案 1 :(得分: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;
}

答案 2 :(得分:0)

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

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

    bool operator==(const Point& other) { return X == other.X && Y == other.Y; };
    
  3. 您的operator<中有一个错误:在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