10

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

我的意思是,我更愿意写

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

代替

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

但我猜标准委员会选择第二个选项是有原因的。

4

2 回答 2

13

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++ 也非常不一致。

于 2013-07-07T21:54:54.527 回答
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);
}
于 2013-07-07T21:35:55.787 回答