只要我可以为我的类型提供哈希函数,为什么我需要为我的类型专门化 std::hash?
As long as I can provide a hasher function for my types why I need to specialize std::hash for my types?
只要我能为我的 class 类型提供我的 hasher
我为什么要专攻 std::hash<my_class_type>
?
我已经实现了这个例子:
class Foo;
template<>
class std::hash<Foo>;
class Foo
{
public:
Foo(std::string const& s, int x) : str_(s), x_(x){}
std::string str()const {return str_;}
int get_x() const{ return x_;}
private:
std::string str_;
int x_{};
friend bool operator==(Foo const&, Foo const&);
friend class std::hash<Foo>;
};
bool operator==(Foo const& lhs, Foo const& rhs)
{
return lhs.str_ == rhs.str_ && lhs.x_ == rhs.x_;
}
size_t hasher(Foo const& f)
{
return std::hash<std::string>()(f.str()) ^
std::hash<int>()(f.get_x());
}
namespace std
{
template <>
class hash<Foo>
{
public:
using argument_type = Foo;
using result_type = std::size_t;
result_type operator()(argument_type const& a)const;
};
typename hash<Foo>::result_type
hash<Foo>::operator()(argument_type const& a)const
{
return hash<std::string>()(a.str_) ^
hash<int>()(a.x_);
}
}
int main()
{
std::unordered_set<Foo, decltype(hasher)*> u(5, hasher); // needs hasher as template argument and as function argument
u.insert(Foo{"hi", 100});
std::unordered_set<Foo> u2; // doesn't need either
u2.insert(Foo("hi", 100));
std::cout << "\ndone!\n";
}
如果我运行程序编译并运行良好。如您所见,我的 hasher
函数与调用运算符中的功能相同 - std::hash<Foo>
特化的成员函数,所以为什么我特化后者 std::hash
而可以提供简单的散列函数?
是为了灵活性吗?例如,为我的 Foo
类型提供一个 hasher
函数而不是专门化 std::hash
需要指定为模板参数和关联容器实例化中的函数参数,例如 unordered_set
而后者不必是例如?
As long as I can provide my hasher for my class types why should I [do I need to] specialize std::hash<my_class_type>
?
您不需要。无论如何,这里有一些这样做的原因:
如果您的 class
是 API 的一部分(内部或外部无关紧要)并且您需要为 class
的用户提供将它用作无序容器中的 Key 的可能性(或出于任何其他原因,散列它),友好的方法是专门化 std::hash
。没有人会期望必须指定一个特殊函数来对(可能记录为)可散列的类型进行散列。
因为它并不比编写实际的哈希算法(只是一个小样板)多多少代码,如果你要多次使用它,你也可以为了你自己而做,或者两次。它使代码更清晰。
一旦你编写了特化,你就不需要自由散列函数,所以你可以立即特化 std::hash
(并完全跳过自由散列函数)如果以上任何一个似乎申请。
使样板文件更短的一种方法是使用 struct
而不是 class
对其进行专门化。主要 class 模板是
namespace std {
template<class Key>
struct hash;
}
所以这样做也很有意义。您的定义将是:
namespace std {
struct hash<Foo> {
using argument_type = Foo;
using result_type = std::size_t;
result_type operator()(argument_type const& a) const;
};
}
Is that for flexibility?
提供专业化不会影响灵活性,但确实会使其更易于使用。
只要我能为我的 class 类型提供我的 hasher
我为什么要专攻 std::hash<my_class_type>
?
我已经实现了这个例子:
class Foo;
template<>
class std::hash<Foo>;
class Foo
{
public:
Foo(std::string const& s, int x) : str_(s), x_(x){}
std::string str()const {return str_;}
int get_x() const{ return x_;}
private:
std::string str_;
int x_{};
friend bool operator==(Foo const&, Foo const&);
friend class std::hash<Foo>;
};
bool operator==(Foo const& lhs, Foo const& rhs)
{
return lhs.str_ == rhs.str_ && lhs.x_ == rhs.x_;
}
size_t hasher(Foo const& f)
{
return std::hash<std::string>()(f.str()) ^
std::hash<int>()(f.get_x());
}
namespace std
{
template <>
class hash<Foo>
{
public:
using argument_type = Foo;
using result_type = std::size_t;
result_type operator()(argument_type const& a)const;
};
typename hash<Foo>::result_type
hash<Foo>::operator()(argument_type const& a)const
{
return hash<std::string>()(a.str_) ^
hash<int>()(a.x_);
}
}
int main()
{
std::unordered_set<Foo, decltype(hasher)*> u(5, hasher); // needs hasher as template argument and as function argument
u.insert(Foo{"hi", 100});
std::unordered_set<Foo> u2; // doesn't need either
u2.insert(Foo("hi", 100));
std::cout << "\ndone!\n";
}
如果我运行程序编译并运行良好。如您所见,我的
hasher
函数与调用运算符中的功能相同 -std::hash<Foo>
特化的成员函数,所以为什么我特化后者std::hash
而可以提供简单的散列函数?是为了灵活性吗?例如,为我的
Foo
类型提供一个hasher
函数而不是专门化std::hash
需要指定为模板参数和关联容器实例化中的函数参数,例如unordered_set
而后者不必是例如?
As long as I can provide my hasher for my class types why should I [do I need to] specialize
std::hash<my_class_type>
?
您不需要。无论如何,这里有一些这样做的原因:
如果您的
class
是 API 的一部分(内部或外部无关紧要)并且您需要为class
的用户提供将它用作无序容器中的 Key 的可能性(或出于任何其他原因,散列它),友好的方法是专门化std::hash
。没有人会期望必须指定一个特殊函数来对(可能记录为)可散列的类型进行散列。因为它并不比编写实际的哈希算法(只是一个小样板)多多少代码,如果你要多次使用它,你也可以为了你自己而做,或者两次。它使代码更清晰。
一旦你编写了特化,你就不需要自由散列函数,所以你可以立即特化 std::hash
(并完全跳过自由散列函数)如果以上任何一个似乎申请。
使样板文件更短的一种方法是使用 struct
而不是 class
对其进行专门化。主要 class 模板是
namespace std {
template<class Key>
struct hash;
}
所以这样做也很有意义。您的定义将是:
namespace std {
struct hash<Foo> {
using argument_type = Foo;
using result_type = std::size_t;
result_type operator()(argument_type const& a) const;
};
}
Is that for flexibility?
提供专业化不会影响灵活性,但确实会使其更易于使用。