如何将 unordered_set 与自定义结构一起使用?
How can I use an unordered_set with a custom struct?
我想使用带有自定义 struct
的 unordered_set
。在我的例子中,自定义 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;
Error C2280 'std::hash<_Kty>::hash(const std::hash<_Kty> &)':
attempting to reference a deleted function
我错过了什么?
std::unordered_set 的第二个模板参数是用于散列的类型。并且在您的情况下将默认为 std::hash<Point>
,这不存在。所以你可以使用 std::unordered_set<Point,Point>
如果散列器是相同的类型。
或者,如果您不想指定散列器,请为 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;
}
虽然上述解决方案可以让您编译代码,但请避免对点使用哈希函数。有一个由 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;
}
我想通过提供更多提示来扩展 :
- 对于您的
struct
,您既不需要定义 operator=
也不需要定义 Point(const Point& other)
,因为您(重新)实现了默认行为。
您可以通过删除 if
子句来简化 operator==
,如下所示:
bool operator==(const Point& other) { return X == other.X && Y == other.Y; };
你的operator<
有一个错误:在else if
子句中,你returntrue
如果两点相等。这违反了 strict weak ordering 的要求。因此,我建议改用下面的代码:
bool operator<(const Point& other) { return X < other.X || (X == other.X && Y < other.Y); };
此外,由于 C++11, you can use 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;
}
但是,正如 , your hash function might not be the best one. Another way to handcraft a hash function 中所解释的那样:
auto hash = [](const Point& p) { return std::hash<int>()(p.X) * 31 + std::hash<int>()(p.Y); };
可以找到更通用的散列解决方案的想法 here。
我想使用带有自定义 struct
的 unordered_set
。在我的例子中,自定义 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;
Error C2280 'std::hash<_Kty>::hash(const std::hash<_Kty> &)': attempting to reference a deleted function
我错过了什么?
std::unordered_set 的第二个模板参数是用于散列的类型。并且在您的情况下将默认为 std::hash<Point>
,这不存在。所以你可以使用 std::unordered_set<Point,Point>
如果散列器是相同的类型。
或者,如果您不想指定散列器,请为 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;
}
虽然上述解决方案可以让您编译代码,但请避免对点使用哈希函数。有一个由 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;
}
我想通过提供更多提示来扩展
- 对于您的
struct
,您既不需要定义operator=
也不需要定义Point(const Point& other)
,因为您(重新)实现了默认行为。 您可以通过删除
if
子句来简化operator==
,如下所示:bool operator==(const Point& other) { return X == other.X && Y == other.Y; };
你的
operator<
有一个错误:在else if
子句中,你returntrue
如果两点相等。这违反了 strict weak ordering 的要求。因此,我建议改用下面的代码:bool operator<(const Point& other) { return X < other.X || (X == other.X && Y < other.Y); };
此外,由于 C++11, you can use 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;
}
但是,正如
auto hash = [](const Point& p) { return std::hash<int>()(p.X) * 31 + std::hash<int>()(p.Y); };
可以找到更通用的散列解决方案的想法 here。