There are several ways.
Specializing std::hash
In your code make sure that your std::hash<Instance>
specialization is preceded immediately by the Instance
class definition, and followed by the use of the unordered_set
container that uses it.
namespace ca
{
class Instance {...};
}
namespaces std {
template<> hash<Instance> { ... };
}
namespace ca {
class InstanceManager
{
// ... some other things.
private unordered_set<Instance>;
}
}
One drawback is that you can have funny name lookup interference when passing a std::hash<ca::Instance>
to other functions. The reason is that the associated namespace (ca
) of all the template arguments of std::hash
can be used during name lookup (ADL). Such errors are a bit rare, but if they occur they can be hard to debug.
See this Q&A for more details.
Passing your hash to unordered_set
struct MyInstanceHash { ... };
using MyUnorderedSet = std:unordered_set<Instance, MyInstanceHash>;
Here, you simply pass your own hash function to the container and be done with it. The drawback is that you have to explicitly type your own container.
Using hash_append
Note, however, there is the N3980 Standard proposal is currently pending for review. This proposal features a much superior design that uses a universal hash function that takes an arbitrary byte stream to be hashed by its template parameter (the actual hashing algorithm)
template <class HashAlgorithm>
struct uhash
{
using result_type = typename HashAlgorithm::result_type;
template <class T>
result_type
operator()(T const& t) const noexcept
{
HashAlgorithm h;
using std::hash_append;
hash_append(h, t);
return static_cast<result_type>(h);
}
};
A user-defined class X then has to provide the proper hash_append
through which it presents itself as a byte stream, ready to be hashed by the univeral hasher.
class X
{
std::tuple<short, unsigned char, unsigned char> date_;
std::vector<std::pair<int, int>> data_;
public:
// ...
friend bool operator==(X const& x, X const& y)
{
return std::tie(x.date_, x.data_) == std::tie(y.date_, y.data_);
}
// Hook into the system like this
template <class HashAlgorithm>
friend void hash_append(HashAlgorithm& h, X const& x) noexcept
{
using std::hash_append;
hash_append(h, x.date_);
hash_append(h, x.data_);
}
}
For more details, see the presentation by the author @HowardHinnant at CppCon14 (slides, video). Full source code by both the author and Bloomberg is available.