我们可以在 C++ 中的 class 中定义 hashcode 方法吗
Can we define hashcode method within a class in C++
我正在尝试用 C++ 实现一个 class,我希望每个 class 都有自己的哈希码实现(基本上将它用作 unordered_map
中的键 & unordered_set
)
例如:
class CustomClass{
int a;
vector<int> b;
string c;
bool operator ==(const CustomClass& o) const{
return ((a == o.a) && (b == o.b) && (c == o.c));
}
/*
Is it possible to define the hashcode function here instead of defining it outside the class.
size_t operator()() const {
// Some custom logic for calculating hash of CustomClass using
// the hash Values of its individual fields
std::size_t h = 0;
for(int& t : b){
h = (h ^ std::hash<int>()(t)) << 1;
}
return (h^(std::hash<int>()(a) << 1))^( std::hash<string>()(c) << 1);
}
*/
};
现在,假设我想在 unordered_map 中使用它,例如
int main(){
unoredered_map<CustomClass, int> m;
}
我有两个选择,
i) 使用模板特化在 std 命名空间中注入哈希码
namespace std {
template <> struct hash<CustomClass> {
size_t operator()(const CustomClass& o) const {
// Some custom logic for calculating hash of CustomClass using
// the hash Values of its individual fields
size_t h = 0;
for(int& t : o.b){
h = (h ^ std::hash<int>()(t)) << 1;
}
return (h^(std::hash<int>()(o.a) << 1))^( std::hash<string>()(o.c) << 1);
}
};
}
或
ii.) 每次实例化时创建unordered_map
(或unordered_set
)时指定此函数,即
struct HashSpecialer {
std::size_t operator()(const CustomClass& o) const {
std::size_t h = 0;
for(int& t : o.b){
h = (h ^ std::hash<int>()(t)) << 1;
}
return (h^(std::hash<int>()(o.a) << 1))^( std::hash<string>()(o.c) << 1);
}
};
并且在实例化 unordered_map
时,我提供了这个结构。
int main(){
unoredered_map<CustomClass, int, HashSpecialer> m;
}
我发现这两种方法使用起来很混乱 (i) 污染了 std 命名空间,并且 (ii) 每次实例化 unordered_map
时都要记得提供 HashSpecializer,这使得使用变得困难
是否可以在 class 定义本身中提供哈希码函数,正如我在上面代码片段的注释部分中所描述的那样
注意:在 java 中,我们可以覆盖 class 中的 hashCode() 方法,我们可以实现此功能。一旦我覆盖了 hashCode() 方法,我以后就不用担心了。
public class CustomClass {
int a;
List<Integer> b;
String c;
// I Let my IDE generate these methods :D
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
CustomClass that = (CustomClass) o;
if (a != that.a)
return false;
if (b != null ? !b.equals(that.b) : that.b != null)
return false;
return c != null ? c.equals(that.c) : that.c == null;
}
// This one too :D
@Override public int hashCode()
{
int result = a;
result = 31 * result + (b != null ? b.hashCode() : 0);
result = 31 * result + (c != null ? c.hashCode() : 0);
return result;
}
}
我正在寻找这样的东西,因为事实证明它非常方便。
我认为解决您的问题的方法是调整您对什么是美观的 C++ 程序的理解。
std::hash
的特化不会污染 std 命名空间,相反您应该考虑 std::hash
是控制 unordered_map
如何与您的 [=20= 一起工作的自定义点].
这样的特化是 class 接口的一部分(并且可以是 class 的友元),其方式与像 operator +()
这样的二元运算符应该是一个非成员函数,仍然是接口的一部分。
I am not sure how do I handle the situation if CustomClass
is defined in more than one namespace
这就是命名空间的用途。您的问题会自行解决 :)
namespace std {
template <> struct hash<NameSpace1::CustomClass> { ... };
template <> struct hash<NameSpace2::CustomClass> { ... };
}
C++ 不是 Java。忘掉任何你知道的 Java,它会极大地帮助你。许多 Java 习语在 C++ 中看起来非常不同,或者在 C++ 中根本无用或不适用。
C++的设计乍一看比较冗长繁琐,但实际上比较灵活。所有 类 不只是 知道哈希码;并且不同的容器可能需要不同的哈希约束,而根本不会侵入包含的类型。
这就是专业化的好处所在。您可以将您的代码挂钩到标准库中而不会实际影响它。毕竟,您专门针对自己的类型,所以它不是侵入性的。
因此,针对您的类型专门化 std::hash()
是完全可以的,只要您确实打算将其用作合理的默认值。否则,您可能需要考虑为 std::unordered_map
和您的密钥类型组合使用别名模板。
我正在尝试用 C++ 实现一个 class,我希望每个 class 都有自己的哈希码实现(基本上将它用作 unordered_map
中的键 & unordered_set
)
例如:
class CustomClass{
int a;
vector<int> b;
string c;
bool operator ==(const CustomClass& o) const{
return ((a == o.a) && (b == o.b) && (c == o.c));
}
/*
Is it possible to define the hashcode function here instead of defining it outside the class.
size_t operator()() const {
// Some custom logic for calculating hash of CustomClass using
// the hash Values of its individual fields
std::size_t h = 0;
for(int& t : b){
h = (h ^ std::hash<int>()(t)) << 1;
}
return (h^(std::hash<int>()(a) << 1))^( std::hash<string>()(c) << 1);
}
*/
};
现在,假设我想在 unordered_map 中使用它,例如
int main(){
unoredered_map<CustomClass, int> m;
}
我有两个选择,
i) 使用模板特化在 std 命名空间中注入哈希码
namespace std {
template <> struct hash<CustomClass> {
size_t operator()(const CustomClass& o) const {
// Some custom logic for calculating hash of CustomClass using
// the hash Values of its individual fields
size_t h = 0;
for(int& t : o.b){
h = (h ^ std::hash<int>()(t)) << 1;
}
return (h^(std::hash<int>()(o.a) << 1))^( std::hash<string>()(o.c) << 1);
}
};
}
或
ii.) 每次实例化时创建unordered_map
(或unordered_set
)时指定此函数,即
struct HashSpecialer {
std::size_t operator()(const CustomClass& o) const {
std::size_t h = 0;
for(int& t : o.b){
h = (h ^ std::hash<int>()(t)) << 1;
}
return (h^(std::hash<int>()(o.a) << 1))^( std::hash<string>()(o.c) << 1);
}
};
并且在实例化 unordered_map
时,我提供了这个结构。
int main(){
unoredered_map<CustomClass, int, HashSpecialer> m;
}
我发现这两种方法使用起来很混乱 (i) 污染了 std 命名空间,并且 (ii) 每次实例化 unordered_map
是否可以在 class 定义本身中提供哈希码函数,正如我在上面代码片段的注释部分中所描述的那样
注意:在 java 中,我们可以覆盖 class 中的 hashCode() 方法,我们可以实现此功能。一旦我覆盖了 hashCode() 方法,我以后就不用担心了。
public class CustomClass {
int a;
List<Integer> b;
String c;
// I Let my IDE generate these methods :D
@Override public boolean equals(Object o)
{
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
CustomClass that = (CustomClass) o;
if (a != that.a)
return false;
if (b != null ? !b.equals(that.b) : that.b != null)
return false;
return c != null ? c.equals(that.c) : that.c == null;
}
// This one too :D
@Override public int hashCode()
{
int result = a;
result = 31 * result + (b != null ? b.hashCode() : 0);
result = 31 * result + (c != null ? c.hashCode() : 0);
return result;
}
}
我正在寻找这样的东西,因为事实证明它非常方便。
我认为解决您的问题的方法是调整您对什么是美观的 C++ 程序的理解。
std::hash
的特化不会污染 std 命名空间,相反您应该考虑 std::hash
是控制 unordered_map
如何与您的 [=20= 一起工作的自定义点].
这样的特化是 class 接口的一部分(并且可以是 class 的友元),其方式与像 operator +()
这样的二元运算符应该是一个非成员函数,仍然是接口的一部分。
I am not sure how do I handle the situation if
CustomClass
is defined in more than one namespace
这就是命名空间的用途。您的问题会自行解决 :)
namespace std {
template <> struct hash<NameSpace1::CustomClass> { ... };
template <> struct hash<NameSpace2::CustomClass> { ... };
}
C++ 不是 Java。忘掉任何你知道的 Java,它会极大地帮助你。许多 Java 习语在 C++ 中看起来非常不同,或者在 C++ 中根本无用或不适用。
C++的设计乍一看比较冗长繁琐,但实际上比较灵活。所有 类 不只是 知道哈希码;并且不同的容器可能需要不同的哈希约束,而根本不会侵入包含的类型。
这就是专业化的好处所在。您可以将您的代码挂钩到标准库中而不会实际影响它。毕竟,您专门针对自己的类型,所以它不是侵入性的。
因此,针对您的类型专门化 std::hash()
是完全可以的,只要您确实打算将其用作合理的默认值。否则,您可能需要考虑为 std::unordered_map
和您的密钥类型组合使用别名模板。