C++ 使用std::unordered_set,值最终与用户提供的散列专门化放在同一个bucket中
我有一段代码,其中我为我的类型C++ 使用std::unordered_set,值最终与用户提供的散列专门化放在同一个bucket中,c++,hash,bucket,unordered-set,C++,Hash,Bucket,Unordered Set,我有一段代码,其中我为我的类型X定义了一个自定义哈希,以便能够将其存储在std::unordered_set中。为此,我使用参数的地址,如下所示 我的第一个问题: 为什么所有的X都在同一个桶中 据我所知: “具有相同哈希代码的键出现在同一个bucket中。” 我还知道,更好的方法是将operator()定义为: auto operator()(X const&X)const{return hash{}(X.m_i);} 但我想知道这里发生了什么。它必须与X{i}是一个临时的(prvalue)有关
X
定义了一个自定义哈希,以便能够将其存储在std::unordered_set
中。为此,我使用参数的地址,如下所示
我的第一个问题:
为什么所有的X
都在同一个桶中
据我所知:
“具有相同哈希代码的键出现在同一个bucket中。
”
我还知道,更好的方法是将operator()
定义为:
auto operator()(X const&X)const{return hash{}(X.m_i);}
但我想知道这里发生了什么。它必须与X{i}
是一个临时的(prvalue)有关-所有X{i}
的地址都一样吗?这是定义还是我在UB土地
我的第二个问题:
当我将noexcept
添加到operator()
时,发生了什么变化
带有noexcept
(g++9.3)的输出:
不同的编译器的输出也不同,例如noexcept
-(clang12.0.0)给出了:
bucket[0]: 0
bucket[1]: 0
bucket[2]: 1
...
bucket[12]: 0
在我看来,您使用的是结构的地址作为散列机制,而不是结构本身。例如,在以下行中:
auto operator()(X const&X)const{return hash{}(&X);}
您将获得一个指向新创建的x
的指针,并基于该指针返回哈希值。现在,之所以将它们都放在同一个bucket中,是因为在for
循环中初始化变量时,编译器使用相同的内存位置。这意味着&x
的结果在循环中始终具有相同的值
为了得到不同的结果,您可能会做两件事。我的建议是将上述行替换为:
auto operator()(X const&X)const{return hash{}(X.m_i);}
但是,你也可能逃脱:
xx1(4);
xx2(5);
usx.插入件(x1);
usx.插入(x2);
因为这可能在不同的地址有
x1
和x2
。这有更大的突破潜力,因为如果给编译器一个高优化级别,内存位置可能最终是相同的。比较相等的对象不也需要生成相同的哈希吗?编辑:要求#4:对于两个相等的参数k1
和k2
,std::hash()(k1)==std::hash()(k2)
。如果散列基于地址,并且如果复制时散列无法更改,则需要确保对象不可复制或分配。然后,如果这样的约束是一个问题,你会看到没有这些操作的后果。@François Andrieux这意味着,使用地址作为散列的一部分从来都不是一个好主意,对吧?@SebastianWilke为了与未排序的容器一起使用它,它永远不会起作用。总的来说,它不太可能有用。散列应该代表对象的值,对象的地址基本上不属于其值的一部分。如果是,则对象也是不可移动和不可压缩的,并且不会与容器一起使用。
bucket[0]: 10
bucket[1]: 0
...
bucket[12]: 0
bucket[0]: 2
bucket[1]: 0
...
bucket[12]: 0
bucket[0]: 0
bucket[1]: 0
bucket[2]: 1
...
bucket[12]: 0