To use a custom type in a std::unordered_set
I have to options.
1) Implement the ==
operator for my type and specialize std::hash
struct MyType {
int x;
bool operator==(const MyType& o) {
return this.x == o.x;
}
};
namespace std
{
template<>
struct hash<MyType> {
size_t operator()(const MyType& o) const {
return hash<int>()(o.x);
}
};
}
std::unordered_set<MyType> mySet;
Or 2), provide functor classes:
struct MyTypeHash {
size_t operator()(const MyType& o) const {
return std::hash<int>()(o.x);
}
};
struct MyTypeCompare {
bool operator()(const MyType& o1, const MyType& o2) const {
return o1.x == o2.x;
}
};
std::unordered_set<MyType, MyTypeHash, MyTypeCompare> mySet;
The second approach lets me choose new behaviour for every new instantion of std::unordered_set
, while with the first approach the behaviour as being part of the type itself will always be the same.
Now, if I know that I only ever want a single behaviour (I'll never define two different comparators for MyType
), which approach is to be preferred? What other differences exist between those two?
Attaching the behavior to the type allows for code like
template<template<class> Set,class T>
auto organizeWithSet(…);
/* elsewhere */ {
organizeWithSet<std::unordered_set,MyType>(…);
organizeWithSet<std::set,MyType>(…);
}
which obviously cannot pass custom function objects.
That said, it is possible to define
template<class T>
using MyUnorderedSet=std::unordered_set<T, MyTypeHash,MyTypeCompare>;
and use that as a template template argument, although that introduces yet another name and might be considered less readable.
Otherwise, you have to consider that your operator==
is simultaneously the default for std::unordered_set
and std::find
, among others; if the equivalence you want for these purposes varies, you probably want named comparators. On the other hand, if one suffices, C++20 might even let you define it merely with =default
.