std::unordered_map<std::string, int> map;
map.find("foo"); // We don't have to construct string("foo") to do the find.
std::hash<long>(-1L) == 18446744073709551615
std::hash<double>(-1.0) == 11078049357879903929
std:unordered_set<double> s;
s.insert(-1L); // Internally converts -1L to -1.0 and hashes that
assert(s.find(-1L) != s.end()); // fails. find calls hash<long>(-1L) and gets the wrong bucket
template<typename T, typename U> void find(std::unordered_set<T> t, U&& u) {
t.find(u);
}
... and secondly, I did not consider a moving erase. Currently, it is impossible to move an element out of a map/set/unordered_map/unordered_set, which is a bit painful IYAM- my previous use cases simply did not require it, so I didn't consider it. Obviously this would have to be move-out-and-erase in one step to prevent violating the invariants of the container.
As technically a splice method is not possible for maps or sets, the goal is to add a new function called remove, which returns a unique pointer holding the node, and add a new overload to insert, allowing to insert the node into the new container
While this would be useful, there is too much chance of two types being equality-comparable but having incompatible hashes. For example, under gcc 8.0,and
std::hash<long>(-1L) == 18446744073709551615
std::hash<double>(-1.0) == 11078049357879903929
If a == b, then I would expect that hash(a) == hash(b). If a language does not exhibit this, then there is something wrong - from a user's perspective hashing is useless if it produces different results for equal values...
Actually, I don't believe that exact use case *is* in the paper. And, whilst it's nice, it would depend on std::hash<const char*> treating it's argument like a C string rather than a generic pointer, and I don't know if it does. If so, then that would certainly be a nice use case- and you could certainly provide a custom hasher for one lookup that could perform this using another of the features in N3573.Whilst I admit that I overlooked implicit conversions in this case, it's not a breaking problem, I'll just have to provide a new name, like, find_by or some such (and the same for erasure). That also means I'd have to use SFINAE to limit operator[] too, since that cannot be given another name, or perhaps some kind of trait. It would have been nice to have something like
template<typename T, typename U> void find(std::unordered_set<T> t, U&& u) {
t.find(u);
}function for any U, I doubt any such code exists, so I'm not that bothered by losing it.
N3573 suggests allowing lookup into unordered sets and maps by alternative keys, as long as operator== can compare both types and the hashes of the two types are compatible. The use case given is
std::unordered_map<std::string, int> map;
map.find("foo"); // We don't have to construct string("foo") to do the find.
Thus, if N3573 were accepted, this code would fail:
std:unordered_set<double> s;
s.insert(-1L); // Internally converts -1L to -1.0 and hashes that
assert(s.find(-1L) != s.end()); // fails. find calls hash<long>(-1L) and gets the wrong bucket
No, no it would not. Consider the original motivation in the paper- unique_ptr<T> and T*. Since there's already a hash specialization for unique_ptr, you could not add one for T* later.
Better would be to use a separate trait to control which types are and are not eligible.
On Apr 17, 2014, at 6:19 PM, Jim Porter <jvp...@g.rit.edu> wrote:
> Maybe I should explain the use case I care about at the moment: I have a map whose key_type is a tuple of value types, but I want to be able to perform lookups on the map with a tuple of reference types. Implementing my own hash function that just combines the hashes of the tuple's members would be totally acceptable for me.
With this proposal:
http://htmlpreview.github.io/?https://github.com/HowardHinnant/papers/blob/master/hashing.html