Some of my favorite alternative hash map implementations are ska::flat_hash_map and ska::bytell_hash_map from https://github.com/skarupke/flat_hash_map. They're fast, and the single header implementation makes them easy to add to a project. For my use cases they generally offer similar performance to abseil and folly F14.
Don't be fooled by the fact that they haven't been updated in ~5 years. I've been using them for nearly that long and have yet to find any bugs.
Check this one out, it's a successor to this idea. Boost also introduced a very performant flat_hash_map in 1.81
And: how often does this even matter? I'd guess very rarely.
In contrast your typical std::unordered_map has indifferent performance with a good hash versus a terrible hash, anything vaguely resembling a working hash will perform similarly (not very well).
Remember "all the defaults are wrong" in C++. That's not literally true, but more often than not, if there's a clownishly obvious mistake possible, C++ made it and you will need to explicitly work around it in your C++ program. In this case, there's no avalanche behaviour in most out-of-box hashes for C++. So the hash for 1, 2, 3 and 4 are extremely similar, and indeed in many cases they turn out to be 1, 2, 3 and 4 respectively. Not a good hash.
[1] https://stackoverflow.com/questions/38304877/why-stdhashint-...
$ python
>>> [hash(x) for x in range(4)]
[0, 1, 2, 3]Turns out for this structure hash quality just doesn't matter very much. The identity function is all you need for mediocre results, and a really great hash produces only slightly improved results with this data structure, yet running the hash costs performance so why bother ?
But a modern hash map structure actually relies on that quality for reasonable performance.
https://martin.ankerl.com/2022/08/27/hashmap-bench-01/#bench...
Ask that table about std::unordered_map - notice the fancier hashes make it worse (bigger numbers are worse, relative to 100 is the best) but not that much worse. It's always poor, it just varies as to whether it's very poor.
Now, ask it about absl::flat_hash_map - for Abseil's own hashes the results range from very good to mediocre, and likewise other modern high quality hashes, but for std::hash it's so awful most benchmarks don't finish, shown as - instead of a number.
You can try to detect this, but this is a fundamentally a QoI problem. Why even provide this rubbish hash function in the standard library? This is a lesson the C++ standard library refuses to learn, if you can't do it well then don't become an attractive nuisance by doing it badly.
People are actually less unhappy with Microsoft's stdlib which gives you a poor FNV hash instead of the identity function. Like, OK, this isn't a great hash, but it does basically work (under non-adversarial conditions, FNV has big problems for tailored input) something like Swiss Tables will be substantially slower than it ought to be, but not crazy benchmark failed due to timeout slower. Nobody is cheering, but it doesn't cause open weeping.
Then it's the good choice - I'm pretty confident the only reason std::hash is explicitly in the standard library is because it's the only way they could find to enable people to specialize hash functions for their own custom types - checking when it was introduced (C++ TR1: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n18...), std::hash is in a subsection of the "unordered containers" section and is really shown as an implementation detail of those (that has to be user-customizable).
If you use another hash table implementation, likely you're also going to choose a hash function that works well with it - I use Ankerl's in my own code for instance which 1/ comes with a modern hash implementation, wyhash, and 2/ obviously I specialize for the really specific cases I need performance for with the known statistical properties of my inputs. But I wouldn't want to pay for this cost for std::unordered_map