Simple code:
#include <unordered_map>
int main()
{
std::unordered_map<const int, int> m;
std::unordered_map<const int, int> m1 = m;
}
produces convoluted compile error message:
Error C2280 'std::hash<_Kty>::hash(void)': attempting to reference a deleted function
which basically says unordered_map
in its internals doesn't expect the key to be constant
PS: I've read the answer for the analogous question:
The associative containers only expose the (key,value) pair as std::pair, so the additional const on the key type is superfluous.
But it doesn't explain why the hashmap with a const key is practically unusable and how to circumvent the issue
Because unordered_map containers do not allow for duplicate keys, this means that the function actually returns 1 if an element with that key exists in the container, and zero otherwise.
We have discussed unordered_map in our previous post, but there is a limitation, we can not store duplicates in unordered_map, that is if we have a key-value pair already in our unordered_multimap and another pair is inserted, then both will be there whereas in case of unordered_map the previous value corresponding to ...
An unordered_map is a hash container, that is, the keys are hashed. Inside of the container, they don't have the same representation as on the outside. Even the name implies that you can't sort it.
For the unordered_map + map , it takes 70 ms for unordered_map insertion and 80 ms for map insertion. So the hybrid implementation is 50 ms faster.
The type
std::unordered_map<const int, int>
uses a defaulted third parameter std::hash<const int>
. This hash type, unlike std::hash<int>
, is not specialized by the standard library, and so is deleted
(as the error message says).
A working hash is required when copying an unordered_set. To make a working hash:
You can specialize std::hash<const int>
by yourself, so that it is no longer deleted:
namespace std
{
// fixes it but is a bad idea - could break in future revisions of the standard
template<>
struct hash<const int> : hash<int>{};
}
Or you can explicitly state your hash:
std::unordered_map<const int, int, std::hash<int>>
Or you can get rid of the const in the key (as it has no effect):
std::unordered_map<int, int>
Addendum:
Deleted means that the constructor of the non-specialized std::hash
is deleted:
template <typename T>
struct hash
{
hash() = delete;
hash(const hash) = delete;
// more deleted methods
};
By "deleted", it means that it does not exist (neither user-provided nor default).
You can see this at cppreference, where they use the terminology of enabled/disabled:
For every type Key for which neither the library nor the user provides an enabled specialization std::hash, that specialization exists and is disabled.
Since std::hash<const int>
is not provided by the library, it is disabled unless it is provided by the user. Next, the text explains what disabled is:
Disabled specializations do not satisfy Hash, [...] std::is_default_constructible_v, std::is_copy_constructible_v [...] are all false. In other words, they exist, but cannot be used.
So, these constructors must be unavailable (and deleting them is the best way to do it).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With