---
tags: c++, c++11, unordered_map
---
# 為什麼unordered_map的key不能是const string
今天同事把一個map replace成unordered_map,因為沒有排序的需求,結果就遇到static assertion
```cpp
static_assert(__check_hash_requirements<_Key, _Hash>::value,
"the specified hash does not meet the Hash requirements");
```
---
我好奇點進去看這個 `__check_hash_requirements` 到底幹了什麼好事
```cpp
template <class _Key, class _Hash>
using __check_hash_requirements = integral_constant<bool,
is_copy_constructible<_Hash>::value &&
is_move_constructible<_Hash>::value &&
__invokable_r<size_t, _Hash, _Key const&>::value
>;
```
感覺怪怪的不知道哪裡有問題,這個問題太難了,只好先問問stackoverflow,果然有找到一篇在問相同問題的
[stackoverflow : Using a const key for unordered_map](https://stackoverflow.com/questions/3998978/using-a-const-key-for-unordered-map)
裡面的回答提到
> The associative containers only expose the (key,value) pair as
std::pair<const key_type, mapped_type>, so the additional const
on the key type is superfluous.
不過看起來他也是亂回答的,因為std::map也是用一樣的pair啊,為什麼std::map就可以用const string當作key?
問題還是出在hash,在仔細看了cppreference後突然有點想法了
---
unordered_map長這樣
```cpp
template<
class Key,
class T,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator< std::pair<const Key, T> >
> class unordered_map;
```
有問題的是這裡的 `std::hash<Key>`,再去查一下 `std::hash`
看到一段是這樣寫著

對比了一下實作,在`<string>`裡對hash的特化是寫成這樣
```cpp
template<class _CharT, class _Traits, class _Allocator>
struct _LIBCPP_TEMPLATE_VIS hash<basic_string<_CharT, _Traits, _Allocator> >
: public unary_function<basic_string<_CharT, _Traits, _Allocator>, size_t>
{
size_t
operator()(const basic_string<_CharT, _Traits, _Allocator>& __val) const _NOEXCEPT;
};
template<class _CharT, class _Traits, class _Allocator>
size_t
hash<basic_string<_CharT, _Traits, _Allocator> >::operator()(
const basic_string<_CharT, _Traits, _Allocator>& __val) const _NOEXCEPT
{
return __do_string_hash(__val.data(), __val.data() + __val.size());
}
```
嗯,沒有const,那只好自己寫一個去呼叫沒有const的版本,結果是可正常執行
```cpp
namespace std {
template <>
struct hash<const std::string> {
size_t operator()(const std::string& str) const noexcept {
std::hash<string> hasher;
return hasher(str);
}
};
}
```
```cpp
int main() {
std::unordered_map<const std::string, int> map {{"a", 1}, {"b", 2}};
std::cout << map["a"] << " " << map["b"] << std::endl;
return 0;
}
```
雖然在一開始發現assertion的時候直接拿掉const就是最快的解決方式,但因為好奇到底為什麼,也意外發現一個stackoverflow上的解答是錯的,也沒有人糾正他。
## 最後我還是把const移除了。
