There's a fairly major deficiency in the interface of the Standard unordered containers. Firstly, you can't look up by types other than the key type. Instead, lookup should be viable for any type where hash(lookup) == hash(stored key), if lookup == stored key. For example, unique_ptr<T> or shared_ptr<T> should be able to be looked up as T*. Secondly, you can't cache the output of the hash function, because it's called internally. Thus, it should provide the ability to provide a different hash function for a specific argument if necessary.
Thus, I propose two new functions to be added to unordered_map, and one to unordered_set (and some const varieties)
template<typename T, typename Hash = std::hash<T>> iterator find(T t, Hash h = Hash());
template<typename T, typename Hash = std::hash<T>> const_iterator find(T t, Hash h = Hash()) const;
Finds the position which corresponds to the argument t. Requires that h(t) == container_hash_argument(key) if t == key for any key in the unordered_map or unordered_set.
template<typename T> value& operator[](T t);
template<typename T> const value& operator[](T t) const;
Returns a [const] reference to the value at the key t. Requires that std::hash<T>()(t) == container_hash_argument(key) if t == key, for any key. If no key is found, an exception must be thrown. Some similar erase() overloads may also be warranted.
This would allow some additional use cases which are currently (needlessly) not permitted as Standard, both of which I have run into in real application code. For example,
std::unordered_set<std::unique_ptr<T>> x;
// fill x
int* ptr = ...;
if (x.find(ptr) == x.end()) {
}
class X {
std::unordered_map<std::string, int>* map;
std::string value = "some value";
bool dirty;
std::size_t hash;
public:
int& lookup() {
// Currently- forcing a needless hash if !dirty
auto it = map->find(value, [this](std::string&) {
if (!dirty) return hash;
else return hash = std::hash<std::string>()(value);
});
return it->second;
}
};
Whilst looking up into a hash container is O(k) in the length of the key, although often referred to as O(1) as it does not depend on the size of the container, this cache scheme can bring it to true O(1), potentially saving many cycles in applications that depend heavily in hash table performance.