我猜测std::hash
被定义为模板结构,以避免在重载函数解析期间完成隐式类型转换。这是正确的说法吗?
我的意思是,我更愿意写
std::string s;
size_t hash = std::hash(s);
而不是
std::string s;
size_t hash = std::hash<std::string>()(s);
但我猜这是标准委员会选择第二种选择的原因。
编辑:修复第二个代码片段。
答案 0 :(得分:10)
部分专门化功能模板是不可能的,因此对于用户定义的模板化类,如果它是一个函数,则无法专门化std::hash
。 (您只能专门化std
命名空间中的模板,但不能超载,因此模板化类的用户无法创建std::unordered_map<MyClass<whatever>, MyOtherClass>
,他们将被迫选择std::unordered_map<MyClass<whatever>, MyOtherClass, ???>
)。因此,functor就是解决方案。
namespace std
{
template<typename T>
struct hash<MyVector<T>>
{
size_t operator()(const MyVector<T>& v)
{
//return hash from here
}
};
}
标准库的替代方法是使用一些SFINAE模板技巧选择成员.hash()
作为默认值,如果是另一种情况则选择标准哈希,但在大多数情况下,您无法改进接口(尤其是如果使用第三方代码)
另一种选择就像std::swap
一样(使用ADL的技巧):
//somewhere in std::unordered_map
using std::hash;
size_t h = hash(key);
根据我的经验,ADL很棘手并不是每个人都记得角落案件。此外,这里的仿函数的优点是你可以将它们用作模板参数,所以你可以插入另一个仿函数(如std::unordered_map<A, B, specialized_hash<A>>
),如果你认为默认情况适合你的情况
来自评论:
但是你能详细说明一下std :: swap吗?它还在那里 在C ++ 11中,它对用户定义的类型没有任何问题,有吗?为什么 在STL中保留许多不同的概念而不是更多 一致?
std::swap
和std::hash
:
在std::hash
:
std::string
哈希对于你的情况来说已经足够了,也就是说,它太通用了,你可以在你的哈希映射中保证你只输入字符串一种,所以你可以提供更快或/和更少碰撞的哈希函数。在std::swap
:
std::swap
复制构造函数。std::vector
可以实现为动态数组,指针隐藏为私有字段,因此您无法访问它们,而不是单独交换它们,甚至无法保证以这种方式实现的事实)std::swap
存在问题:标准容器提供swap
成员函数,std::swap
可以是专用的(但仅适用于非模板化类),并且可以定义交换作为ADL发现的自由功能。你应该如何提供自己的交换? IMO令人困惑,而不是std::swap
是函数而std::hash
是函数的事实。为什么STL不一致?我只能在这里猜测,但STL不一致的主要原因是(a)bawkward兼容性和(b)C ++也非常不一致。
答案 1 :(得分:4)
一个可能的原因是,通过这种方式更容易在模板中使用它作为默认的可更改选项
template <typrname T, typename = std::hash<T>...>
class unordered_set;
顺便说一句,你可以创建以这种方式工作的功能
template<typename T, typename... Args>
auto hasher(Args&&... args) -> whatever {
return std::hash<T>(std::forward<Args>(args)...)( //maybe some &'s skipped
}
或(允许检测类型)
template<typename T, typename... Args>
auto hasher(T t) -> whatever {
return std::hash<T>()(t);
}