【发布时间】:2021-12-31 06:09:45
【问题描述】:
为什么std::hash<const std::string> 不专攻std?
它会导致编译错误
std::unordered_map<const std::string, int> m;
m.insert(std::make_pair("Foo", 1)); //error
m["Bar"] = 2; // also error
这有什么原因吗?
【问题讨论】:
为什么std::hash<const std::string> 不专攻std?
它会导致编译错误
std::unordered_map<const std::string, int> m;
m.insert(std::make_pair("Foo", 1)); //error
m["Bar"] = 2; // also error
这有什么原因吗?
【问题讨论】:
为什么
std::hash<const std::string>不专攻std?
事实上,std 没有专门化任何std::hash<const T>。原因可能是不需要它。对于任何std::hash<T>,它唯一的函数是operator(),它接受const T& 作为它的参数。因此,即使std::hash<const Foo> 是特化的,它也将与std::hash<Foo> 完全相同。
然后回到你所困扰的代码,它真的应该是:
std::unordered_map<std::string, int> m;
这里,尽管key_type是std::string,但是key的实际类型其实是const std::string,可以用decltype(m)::value_type::first_type访问。
当你想到散列的概念时,不应该改变键,所以 std::hash 的规范看起来比 std::hash 更合适。你不同意吗?
确定哈希函数不会也不应该改变密钥,但同时它可能不应该复制,那么为什么不去std::hash<const T&>呢?
需要注意的一点是,被专门化的类型并不总是等于参数类型。模板参数更多的是关于哈希函数与什么类型相关,而不是传递给调用运算符的内容。同样,numeric_limits<T> 期望非 cv 限定的数字类型为类型 T。这并不意味着const int 和const double 没有各自的限制。除非您期望 hash<T> 和 hash<const T> 有不同的行为,否则为什么要对它们进行两次专门化?
还有一点需要注意的是std::hash 实际上更像是一个与unordered_XXX 系列捆绑在一起的实用程序类,并且默认主要用作无序系列的散列函数,或用于定义您的自定义散列将自定义类型提供给无序族的函数。
【讨论】:
T 类型始终可以隐式转换为 const T,但反过来则不然。