【发布时间】:2021-06-01 13:13:41
【问题描述】:
我有这段代码,我在其中为我的类型X 定义了一个自定义哈希,以便能够将它存储在std::unordered_set 中。为此,我使用如下所示的参数地址。
我的第一个问题:
为什么所有
X'es都在同一个桶中?
来自26.2.7 Unordered associative containers 我知道:
"Keys with the same hash code appear in the same bucket."
我也知道更好的方法是将operator() 定义为:
auto operator()(X const &x) const { return hash<int>{}(x.m_i); },
但我想了解这里发生了什么。它与X{i} 是一个临时的(prvalue)有关 - 所有X{i} 的地址都相同吗?这是定义的还是我在UB-land?
我的第二个问题:
当我将
noexcept添加到operator()时发生了什么变化/发生了什么?
#include <iostream>
#include <ostream>
#include <unordered_set>
class X
{
int m_i;
public:
explicit X(int i) : m_i{i} {}
auto operator==(X const &x) const { return m_i == x.m_i; }
};
template <>
struct std::hash<X> {
auto operator()(X const &x) const { return hash<X const *>{}(&x); }
// what's the difference to above version?
// auto operator()(X const &x) const noexcept { return hash<X const *>()(&x); }
// better:
// auto operator()(X const &x) const { return hash<int>{}(x.m_i); } // better
};
int main()
{
std::unordered_set<X> usx;
for (int i{}; i < 10; ++i)
usx.insert(X{i});
for (std::size_t index{}; index < usx.bucket_count(); ++index)
std::cout << "bucket[" << index << "]: " << usx.bucket_size(index) << '\n';
}
没有noexcept 的输出(g++ 9.3):
bucket[0]: 10
bucket[1]: 0
...
bucket[12]: 0
noexcept (g++ 9.3) 的输出:
bucket[0]: 2
bucket[1]: 0
...
bucket[12]: 0
输出也因不同的编译器而异 - 使用 noexcept - (clang 12.0.0) 例如给出:
bucket[0]: 0
bucket[1]: 0
bucket[2]: 1
...
bucket[12]: 0
【问题讨论】:
-
不是比较相等的对象也需要产生相同的散列吗?编辑:
std::hash要求 #4:对于两个相等的参数k1和k2,std::hash<Key>()(k1) == std::hash<Key>()(k2)。 -
如果哈希是基于地址的,并且复制后哈希不会改变,则需要确保对象不可复制或不可分配。然后,如果这样的约束是一个问题,你会看到没有这些操作的后果。
-
@François Andrieux 这意味着,使用地址作为哈希的一部分无论如何都不是一个好主意,对吧?
-
@SebastianWilke 为了将它与未排序的容器一起使用,它永远不会起作用。总的来说,它不太可能有用。散列应该代表对象的值,而对象的地址基本上绝不是其值的一部分。如果是,则该对象也是不可移动和不可复制的,并且不会与容器一起使用。
标签: c++ hash bucket unordered-set