<xstd/misc/functional/hash.hpp>¶
xstd::hash¶
// (1)
template<class T, hash_algorithm H64 = boost::hash2::xxhash_64, hash_algorithm H32 = H64,
class Flavor = boost::hash2::default_flavor>
requires (sizeof(typename H32::result_type) <= sizeof(typename H64::result_type))
class hash;
// (2)
template<class CharT, class Traits, class Allocator, hash_algorithm H64, hash_algorithm H32, class Flavor>
class hash<basic_string<CharT, Traits, Allocator>, H64, H32, Flavor>;
// (3)
template<class T, hash_algorithm H64, hash_algorithm H32, class Flavor>
class hash<optional<T>, H64, H32, Flavor>;
// (4)
template<class T, class Allocator, hash_algorithm H64, hash_algorithm H32, class Flavor>
class hash<indirect<T, Allocator>, H64, H32, Flavor>;
An unordered container's hash for keys of type T, from a seeded Boost.Hash2 algorithm. The class holds a prototype
of hash_type, seeded once; each call copies the prototype, appends the key and folds the result to size_t.
- The primary template.
- The string specialization, transparent over
basic_string_view<CharT, Traits>. A string, its view and a C string hash the same, and a set of strings finds a key by its view, or by a literal, without building a string. Only a string key is transparent: equal values of other types need not append the same bytes. - The optional specialization. An engaged optional hashes as
hash<remove_const_t<T>, H64, H32, Flavor>hashes its value, as [optional.hash] requires, and an empty one as no bytes at all, which no key appends. It is seeded as that hash is, andis_avalanchingis that hash's. - The indirect specialization, where the standard library declares
indirect. Anindirecthashes ashash<T, H64, H32, Flavor>hashes its value, as [indirect.hash] requires, and a valueless one as no bytes at all.
A key's own hash is its Boost.Hash2 tag_invoke, which every key containing it follows at any depth. A
specialization of hash for a key is followed only by the optional and indirect specializations.
Keys¶
Every key Boost.Hash2's hash_append hashes, and every key the standard specializes std::hash for. Hash2 1.92
hashes the arithmetic, enumeration and pointer types, nullptr_t, strings and their views, ranges, tuple-likes,
optional, variant and monostate. For the other standard keys, the header appends the state each one's
operator== reads:
| Key | Appends |
|---|---|
a floating-point type of neither 4 nor 8 bytes, such as x87's long double, float16_t or float128_t |
its value bits, with -0 as +0 and without padding |
__int128, unsigned __int128, where the compiler has them and the key is not inside another |
its low and high 64 bits |
bitset<N> |
its bits, 64 to a word |
error_code, error_condition |
the address of its category, then its value |
unique_ptr<T, D>, shared_ptr<T> |
get() |
type_index |
name() |
thread::id |
its object representation |
coroutine_handle<P> |
address() |
filesystem::path |
its root name, whether it has a root directory, then each element of its relative path |
stacktrace_entry |
native_handle() |
text_encoding |
mib(), then for id::other its name, lowercased, without non-alphanumerics or 0s |
indirect<T, Allocator> |
whether it is valueless, then its value |
chrono::duration, chrono::time_point |
count(), time_since_epoch() |
chrono::day, month, year, weekday and the calendar types built from them |
each field operator== compares |
chrono::zoned_time, chrono::leap_second |
its time zone and its time, its date |
Equal keys therefore hash equal, and unequal keys append different bytes. A key whose state is an address, such as a smart pointer or an error's category, hashes differently from one run to the next.
Template parameters¶
| Parameter | Meaning |
|---|---|
T |
The key type. An argument converts to T before it is appended, so 42 hashes as the int64_t a table holds. |
H64 |
The algorithm where size_t is wider than 32 bits. Constrained to model hash_algorithm, so a second key type in its place is rejected. Defaults to xxhash_64. |
H32 |
The algorithm where size_t is at most 32 bits, constrained as H64 is. Defaults to H64, so one algorithm named alone runs on every target. |
Flavor |
The flavor Hash2 appends with. Defaults to default_flavor, which appends in the target's own byte order. |
Remarks: A pair whose 32-bit algorithm is named first, such as xxhash_32, xxhash_64, is rejected by the
requires-clause.
Member types¶
| Type | Definition |
|---|---|
hash_type |
conditional_t<sizeof(size_t) <= sizeof(uint32_t), H32, H64>, the algorithm that runs |
is_avalanching |
bool_constant<hash_algorithm_is_avalanching_v<hash_type>>, which boost::hash_is_avalanching reads |
is_transparent |
(2) only |
Member functions¶
| Function | Purpose |
|---|---|
| (constructor) | value-initializes or seeds the prototype |
operator() |
hashes a key |
(constructor)¶
hash() = default; // (1)
constexpr explicit hash(uint64_t seed); // (2)
constexpr hash(const unsigned char* p, size_t n); // (3)
- Value-initializes the prototype.
- Initializes it from
seed. - Initializes it from the
nbytes atp.
Remarks: Each is a constant expression.
operator()¶
constexpr auto operator()(const T& v) const -> size_t;
The string specialization has two in its place: one taking const basic_string<CharT, Traits, Allocator>&, which
accepts whatever converts to a string, as std::hash's parameter does, and one taking any const K& that converts
to basic_string_view<CharT, Traits>, such as a view or a C string, which hashes as the view. The optional
specialization takes const optional<T>& and the indirect one const indirect<T, Allocator>&.
- Effects: Copies the prototype to
hand callsboost::hash2::hash_append(h, Flavor(), v). The prototype is unchanged. - Returns:
boost::hash2::get_integral_result<size_t>(h). - Throws: Any exception the key's
hash_appendthrows. - Remarks: It is
noexceptwhere [unord.hash]/3 makesstd::hash<T>'s callnoexcept, andhash_typeis one of Hash2's own algorithms. That covers the arithmetic, enumeration and pointer types,nullptr_t, strings and their views, and each standard key the Keys table lists butindirect,unique_ptr,duration,time_pointandzoned_time. The optional and indirect specializations arenoexceptwhere the hash of their value is. - Remarks: It is unconstrained: Hash2 declares
hash_appendfor every type, so a constraint would admit keys it then rejects, such asweak_ptr. - Remarks: As of Boost.Hash2 1.92 a call is not a constant expression, since
get_integral_resultis notconstexpr.
Notes¶
- Equal values hash equal, and unequal values append different bytes.
- It serves as the hash of
unordered_setandboost::unordered_flat_set, seeded per container. - Under FNV-1a, keys that differ only in their high bits share a bucket wherever a table masks the hash, and spread
in a
boost::unordered_flat_set, which mixes it first.
Example¶
Adapted from the example of std::hash on cppreference.com, at
revision 179088, by cppreference.com
contributors. This example is under CC-BY-SA 3.0 and the GFDL, as
cppreference.com's content is; the rest of this page is under the Boost Software License.
#include <xstd/misc/functional/hash.hpp> // hash
#include <boost/hash2/hash_append.hpp> // hash_append_tag
#include <boost/hash2/siphash.hpp> // siphash_64
#include <cstdint> // uint64_t
#include <iomanip> // quoted
#include <iostream> // boolalpha, cout
#include <string> // string
#include <string_view> // string_view
#include <unordered_set> // unordered_set
struct S
{
std::string first_name;
std::string last_name;
[[nodiscard]] auto operator==(S const&) const -> bool = default;
// A key says what it hashes, its members in order, and leaves the algorithm to the hasher.
template<class Provider, class Hash, class Flavor>
friend constexpr auto tag_invoke(boost::hash2::hash_append_tag const&, Provider const& provider, Hash& h, Flavor const& f, S const* s)
-> void
{
provider.hash_append(h, f, s->first_name);
provider.hash_append(h, f, s->last_name);
}
};
// A hasher can name its algorithm: SipHash, seeded, where an adversary chooses the keys.
using siphasher = xstd::hash<S, boost::hash2::siphash_64>;
auto main() -> int
{
auto const str = std::string("Meet the new boss...");
auto const str_hash = xstd::hash<std::string>()(str);
std::cout << std::boolalpha << "hash(" << std::quoted(str) << ") == hash of its view: "
<< (str_hash == xstd::hash<std::string>()(std::string_view(str))) << '\n';
// A fixed seed, for the example; a program draws one at random, per container.
auto const seed = std::uint64_t{0x9e37'79b9'7f4a'7c15};
auto const obj = S{"Hubert", "Farnsworth"};
auto const copy = obj;
std::cout << "hash(" << std::quoted(obj.first_name) << ", " << std::quoted(obj.last_name) << ") == hash of a copy: "
<< (xstd::hash<S>()(obj) == xstd::hash<S>()(copy)) << " (using xxhash_64) and "
<< (siphasher(seed)(obj) == siphasher(seed)(copy)) << " (using a seeded siphash_64)\n";
// A key that appends its members can be used in unordered containers, under whichever algorithm they name.
auto const names = std::unordered_set<S, siphasher>({obj, {"Bender", "Rodriguez"}, {"Turanga", "Leela"}}, 0, siphasher(seed));
for (auto const& s : {S{"Bender", "Rodriguez"}, S{"Turanga", "Leela"}, obj, S{"Philip", "Fry"}}) {
std::cout << std::quoted(s.first_name) << ' ' << std::quoted(s.last_name) << " is in the set: " << names.contains(s) << '\n';
}
}
Output:
hash("Meet the new boss...") == hash of its view: true
hash("Hubert", "Farnsworth") == hash of a copy: true (using xxhash_64) and true (using a seeded siphash_64)
"Bender" "Rodriguez" is in the set: true
"Turanga" "Leela" is in the set: true
"Hubert" "Farnsworth" is in the set: true
"Philip" "Fry" is in the set: false
See also¶
| Entity | Relation |
|---|---|
digest |
a key's digest at its algorithm's own width, from the same seeded prototype |
hash_algorithm |
what H64 and H32 model |
hash_algorithm_is_avalanching |
what is_avalanching reads |
std::hash |
the standard's hash, whose role this class generalizes |