为什么std :: hash不是重载函数?

时间:2013-07-07 21:34:06

标签: c++11 stdhash

我猜测std::hash被定义为模板结构,以避免在重载函数解析期间完成隐式类型转换。这是正确的说法吗?

我的意思是,我更愿意写

std::string s;
size_t hash = std::hash(s);

而不是

std::string s;
size_t hash = std::hash<std::string>()(s);

但我猜这是标准委员会选择第二种选择的原因。

编辑:修复第二个代码片段。

2 个答案:

答案 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::swapstd::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);
}