カスタムstruct
でunordered_set
を使用したいと思います。私の場合、カスタムstruct
は、ユークリッド平面の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 <_Kty> :: hash(const std :: hash <_Kty>&) ':削除された関数を参照しようとしています
何が足りないのですか?
Std :: unordered_setの2番目のテンプレートパラメータは、ハッシュに使用するタイプです。あなたの場合、デフォルトで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) {
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
によってパラメータ化された1次元部分空間があり、そのために線上のすべての点がy = -x/10 + b
は同じハッシュ値を持ちます。たとえば、上位32ビットがx座標で、下位32ビットがy座標である64ビットハッシュを使用したほうがよいでしょう。それは次のようになります
uint64_t hash(Point const & p) const noexcept
{
return ((uint64_t)p.X)<<32 | (uint64_t)p.Y;
}
rmawatsonの回答 をさらにいくつかのヒントを提供して拡張したいと思います。
struct
については、デフォルトの動作を(再)実装したため、_operator=
_もPoint(const Point& other)
も定義する必要はありません。次のようにif
句を削除することで、_operator==
_を合理化できます。
_bool operator==(const Point& other) { return X == other.X && Y == other.Y; };
_
_operator<
_に誤りがあります。_else if
_句で、両方の点が等しい場合はtrue
を返します。これは、 厳密な弱順序 の要件に違反します。したがって、代わりに次のコードを使用することをお勧めします。
_bool operator<(const Point& other) { return X < other.X || (X == other.X && Y < other.Y); };
_
さらに、 C++ 11 なので、ハッシュ関数と比較関数を定義する代わりに ラムダ式 を使用できます。このように、他の方法で演算子が必要ない場合は、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の回答 でも説明されているように、ハッシュ関数は最適な関数ではない可能性があります。 ハッシュ関数を手作りする の別の方法は次のとおりです。
_auto hash = [](const Point& p) { return std::hash<int>()(p.X) * 31 + std::hash<int>()(p.Y); };
_
ハッシュのより一般的な解決策のアイデアは、 ここ にあります。