124 lines
3.4 KiB
Plaintext
124 lines
3.4 KiB
Plaintext
////
|
|
Copyright 2005-2008 Daniel James
|
|
Copyright 2022 Christian Mazakas
|
|
Copyright 2022 Peter Dimov
|
|
Distributed under the Boost Software License, Version 1.0.
|
|
https://www.boost.org/LICENSE_1_0.txt
|
|
////
|
|
|
|
[#combine]
|
|
= Combining Hash Values
|
|
:idprefix: combine_
|
|
|
|
Say you have a point class, representing a two dimensional location:
|
|
|
|
[source]
|
|
----
|
|
class point
|
|
{
|
|
int x;
|
|
int y;
|
|
|
|
public:
|
|
|
|
point() : x(0), y(0) {}
|
|
point(int x, int y) : x(x), y(y) {}
|
|
|
|
bool operator==(point const& other) const
|
|
{
|
|
return x == other.x && y == other.y;
|
|
}
|
|
};
|
|
----
|
|
|
|
and you wish to use it as the key for an `unordered_map`. You need to
|
|
customise the hash for this structure. To do this we need to combine the
|
|
hash values for `x` and `y`. The function `boost::hash_combine` is supplied
|
|
for this purpose:
|
|
|
|
[source]
|
|
----
|
|
class point
|
|
{
|
|
...
|
|
|
|
friend std::size_t hash_value(point const& p)
|
|
{
|
|
std::size_t seed = 0;
|
|
|
|
boost::hash_combine(seed, p.x);
|
|
boost::hash_combine(seed, p.y);
|
|
|
|
return seed;
|
|
}
|
|
|
|
...
|
|
};
|
|
----
|
|
|
|
Calls to `hash_combine` incrementally build the hash from the different
|
|
members of `point`, it can be repeatedly called for any number of elements.
|
|
It calls `hash_value` on the supplied element, and combines it with the seed.
|
|
|
|
Full code for this example is at link:../../examples/point.cpp[examples/point.cpp].
|
|
|
|
Note that when using `boost::hash_combine` the order of the calls matters.
|
|
|
|
[source]
|
|
----
|
|
std::size_t seed = 0;
|
|
boost::hash_combine(seed, 1);
|
|
boost::hash_combine(seed, 2);
|
|
----
|
|
|
|
and
|
|
|
|
[source]
|
|
----
|
|
std::size_t seed = 0;
|
|
boost::hash_combine(seed, 2);
|
|
boost::hash_combine(seed, 1);
|
|
----
|
|
|
|
result in a different values in `seed`.
|
|
|
|
To calculate the hash of an iterator range you can use `boost::hash_range`:
|
|
|
|
[source]
|
|
----
|
|
std::vector<std::string> some_strings;
|
|
std::size_t hash = boost::hash_range(some_strings.begin(), some_strings.end());
|
|
----
|
|
|
|
Since `hash_range` works by repeatedly invoking `hash_combine` on the elements
|
|
of the range, the hash value will also be dependent on the element order.
|
|
|
|
If you are calculating a hash value for a range where the order of the data
|
|
doesn't matter, such as `unordered_set`, you can use
|
|
`boost::hash_unordered_range` instead.
|
|
|
|
[source]
|
|
----
|
|
std::unordered_set<std::string> set;
|
|
std::size_t hash = boost::hash_unordered_range(set.begin(), set.end());
|
|
----
|
|
|
|
When writing template classes, you might not want to include the main
|
|
`hash.hpp` header as it's quite an expensive include that brings in a lot of
|
|
other headers, so instead you can include the
|
|
`<boost/container_hash/hash_fwd.hpp>` header which forward declares
|
|
`boost::hash`, `boost::hash_combine`, `boost::hash_range`, and
|
|
`boost::hash_unordered_range`. You'll need to include the main header before
|
|
instantiating `boost::hash`. When using a container that uses `boost::hash` it
|
|
should do that for you, so your type will work fine with the Boost hash
|
|
containers. There's an example of this in
|
|
link:../../examples/template.hpp[examples/template.hpp] and
|
|
link:../../examples/template.cpp[examples/template.cpp].
|
|
|
|
To avoid including even `hash_fwd.hpp` - which still requires the contents
|
|
of Boost.ContainerHash to be physically present - you are allowed to copy the
|
|
declarations from `hash_fwd.hpp` (and only those) directly into your own
|
|
header. This is a special exception guaranteed by the library; in general,
|
|
you can't declare library functions, Boost or otherwise, without risk of
|
|
breakage in a subsequent release.
|