////
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.
