【发布时间】:2020-11-12 10:06:29
【问题描述】:
以下代码导致对哈希函数的不明调用:
namespace foo {
using Position = tuple <int, int, int>;
std::ostream& operator<<(std::ostream& out, const Position& pos) noexcept{
return out << get<0>(pos) << ", " << get<1>(pos) << ", " << get<2>(pos);
}
struct hashFunc{
std::size_t operator()(const Position& pos) const noexcept{
int res = get<0>(pos) * 17 ^ get<1>(pos) * 11 ^ get<2>(pos);
cout << "@@@ hash function called for key: " << pos
<< ", hash: " << res << endl;
return res;
}
};
template<typename T>
void print_buckets(T&& map) {
auto num_buckets = map.bucket_count();
cout << "------------------------------" << endl;
cout << "NUM BUCKETS: " << num_buckets << endl;
for(size_t i=0; i<num_buckets; ++i) {
auto bucket_size = map.bucket_size(i);
if(bucket_size) {
cout << "BUCKET " << i << " size: " << bucket_size << endl;
}
}
cout << "------------------------------" << endl;
}
}
主要:
using namespace foo;
int main() {
// note: bucket_count specified
unordered_map <Position, std::string, hashFunc> test(10);
auto x = tuple{1,0,0};
auto z = tuple{0,1,0};
auto w = tuple{0,0,1};
cout << "==================================" << endl;
cout << "about to insert: " << x << endl;
test[x] = "hello";
print_buckets(test);
cout << "after insert of: " << x << endl;
cout << "==================================" << endl;
cout << "about to insert: " << z << endl;
test[z] = "hey";
print_buckets(test);
cout << "after insert of: " << z << endl;
cout << "==================================" << endl;
cout << "about to insert: " << w << endl;
test.insert({w, "hello"});
print_buckets(test);
cout << "after insert of: " << w << endl;
cout << "==================================" << endl;
}
输出:
==================================
about to insert: 1, 0, 0
@@@ hash function called for key: 1, 0, 0, hash: 17
------------------------------
NUM BUCKETS: 11
BUCKET 6 size: 1
------------------------------
after insert of: 1, 0, 0
==================================
about to insert: 0, 1, 0
@@@ hash function called for key: 0, 1, 0, hash: 11
@@@ hash function called for key: 1, 0, 0, hash: 17 <= why?
------------------------------
NUM BUCKETS: 11
@@@ hash function called for key: 1, 0, 0, hash: 17 <= why?
BUCKET 0 size: 1
BUCKET 6 size: 1
------------------------------
after insert of: 0, 1, 0
==================================
about to insert: 0, 0, 1
@@@ hash function called for key: 0, 0, 1, hash: 1
@@@ hash function called for key: 0, 1, 0, hash: 11 <= why?
------------------------------
NUM BUCKETS: 11
@@@ hash function called for key: 1, 0, 0, hash: 17 <= why?
BUCKET 0 size: 1
@@@ hash function called for key: 0, 1, 0, hash: 11 <= why?
BUCKET 1 size: 1
BUCKET 6 size: 1
------------------------------
after insert of: 0, 0, 1
==================================
Code (gcc 和 clang 的行为相同)
注意事项:
1。在没有bucket_count 构造函数参数的情况下尝试相同的方法,由于重新散列,对散列函数的调用变得更加过度。但是在上面的场景中,似乎没有rehash,也没有碰撞。
2。相关,但特别是在 MSVC 上:Inserting to std::unordered_map calls hash function twice in MSVC++'s STL, bad design or special reason?
【问题讨论】:
-
你是在release还是-O2编译的
-
@Surt 具有上述行为的 -O2 和 -O3 结果
-
仅供参考:
std::endl只是"\n"的(有时)较慢的版本。它在输出"\n"之后执行flush,这通常不是必需的。 -
@HTNW 在尝试分析事件序列的代码中,
flush可能是必要的,尤其是在某些事情失败并且程序终止的情况下。 -
请注意,只有在使用 libstdc++ 时,Clang 的行为才相同。使用 libc++ 就没有这样的问题:godbolt.org/z/z56EeE.
标签: c++ unordered-map