Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/161.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用std::unordered_set,值最终与用户提供的散列专门化放在同一个bucket中_C++_Hash_Bucket_Unordered Set - Fatal编程技术网

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