diff --git a/doc/hash/combine.adoc b/doc/hash/combine.adoc index 3bd3575..ac49a86 100644 --- a/doc/hash/combine.adoc +++ b/doc/hash/combine.adoc @@ -2,3 +2,79 @@ = Combining hash values :idprefix: combine_ + +Say you have a point class, representing a two dimensional location: + +[listing] +---- +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 xref:#ref_hash_combine[boost::hash_combine] is supplied for this purpose: + +[listing,subs="+quotes,+macros"] +---- +class point +{ + ... + + friend std::size_t hash_value(point const& p) + { + std::size_t seed = 0; + xref:#ref_hash_combine[boost::hash_combine](seed, p.x); + xref:#ref_hash_combine[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 xref:#ref_hash_value[hash_value] on the supplied element, and combines it with the seed. + +Full code for this example is at link:../../examples/point.cpp[/libs/container_hash/examples/point.cpp^]. + +[NOTE] +==== +When using xref:#ref_hash_combine[boost::hash_combine] the order of the calls matters. +[listing,subs="+quotes,+macros"] +---- +std::size_t seed = 0; +boost::hash_combine(seed, 1); +boost::hash_combine(seed, 2); +---- +results in a different seed to: + +[listing,subs="+quotes,+macros"] +---- +std::size_t seed = 0; +boost::hash_combine(seed, 2); +boost::hash_combine(seed, 1); +---- + +If you are calculating a hash value for data where the order of the data doesn't matter in comparisons (e.g. a set) you will have to ensure that the data is always supplied in the same order. + +==== + +To calculate the hash of an iterator range you can use xref:#ref_hash_range[boost::hash_range]: + +[listing,subs="+quotes,+macros"] +---- +std::vector some_strings; +std::size_t hash = xref:#ref_hash_range[boost::hash_range](some_strings.begin(), some_strings.end()); +---- + +Note that when writing template classes, you might not want to include the main hash header as it's quite an expensive include that brings in a lot of other headers, so instead you can include the `` header which forward declares xref:#ref_hash[boost::hash], xref:#ref_hash_range[boost::hash_range] and xref:#ref_hash_combine[boost::hash_combine]. You'll need to include the main header before instantiating xref:#ref_hash[boost::hash]. When using a container that uses xref:#ref_hash[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[template.hpp^] and link:../../examples/template.cpp[template.cpp^].