我猜std::hash
被定义为模板结构,以避免在重载函数解析期间进行隐式类型转换。这是正确的说法吗?
我的意思是,我更愿意写
std::string s;
size_t hash = std::hash(s);
代替
std::string s;
size_t hash = std::hash<std::string>()(s);
但我猜标准委员会选择第二个选项是有原因的。
std::hash
部分特化函数模板是不可能的,因此对于用户定义的模板类,如果它是一个函数,就没有办法特化。(您只能从std
命名空间专门化模板,但不能重载,因此模板类的用户无法创建std::unordered_map<MyClass<whatever>, MyOtherClass>
,他们将被迫选择std::unordered_map<MyClass<whatever>, MyOtherClass, ???>
)。所以函子是这里的解决方案。
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 中的自由函数。你应该如何提供你自己的交换?std::swap
令人困惑的IMO,而不是函数和std::hash
函子的事实。为什么 STL 不一致?我只能在这里猜测,但 STL 不一致的主要原因是 (a) 不兼容的兼容性和 (b) C++ 也非常不一致。
一个可能的原因是,通过这种方式,通过可更改选项默认在模板中使用它更容易
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);
}