插入 std::unordered_map 在 MSVC++ 的 STL 中调用哈希函数两次,设计不好还是特殊原因?
Inserting to std::unordered_map calls hash function twice in MSVC++'s STL, bad design or special reason?
对于此代码:
#include<unordered_map>
#include<iostream>
using namespace std;
struct myhash {
unsigned operator()(const unsigned&v)const {
cout<<"Hash function is called:"<<v<<endl;
return v;
}
};
unordered_map<unsigned,unsigned,myhash>mp;
int main() {
for (unsigned i=0;i<3;++i) {
cout<<"Visiting hash table:"<<i<<endl;
++mp[i];
}
}
当使用g++时,输出并不奇怪:
Visiting hash table:0
Hash function is called:0
Visiting hash table:1
Hash function is called:1
Visiting hash table:2
Hash function is called:2
但MSVC++(2015)的输出震惊了我:
Visiting hash table:0
Hash function is called:0
Hash function is called:0
Visiting hash table:1
Hash function is called:1
Hash function is called:1
Visiting hash table:2
Hash function is called:2
Hash function is called:2
进一步的测试表明,当在无序映射中插入新元素时,MSVC++的STL调用散列函数两次,如果元素已经在映射中,则调用一次。
我对这种行为感到很奇怪,因为我认为在大多数情况下,缓存结果比再次调用哈希函数快得多(如果实现真的需要使用两次哈希值)。或者更好的是,我认为只使用一次散列函数来查找元素的bucket,而下面的例程与散列值无关。
然而,由于STL的作者在C++方面比我更有经验,我不知道他们是否有一些特殊的原因来实现这个实现?这只是一个糟糕的设计,还是由一些我不知道的原因引起的?
在发布模式下也观察到了VS2012的相同行为。
不同之处在于unorderedmap的实现。如果调试并进入:Microsoft Visual Studio 11.0\VC\include\undered_map,则对函数调用运算符的第一个调用是检查键是否已存在于映射中。第二个调用是在插入映射期间进行的(前提是找不到密钥)-在3次迭代后,映射具有((0,1),(1,1),(2,1))
如果我们看看23.5.4.3,效果子句和复杂性子句似乎都不需要只调用一次散列函数。看起来,虽然直觉上很奇怪,但这两种实现都是合法的。
相关文章:
- g++的分段错误(在NaN上使用to_string两次时)
- 蛇在C++不会连续转两次
- 检查一个数组是否包含在另一个数组中,以相反的顺序,至少两次
- 从具有按值捕获的 lambda 移动构造 std::函数时,移动构造函数调用两次
- 我应该如何去缓解两次出现的cin?
- Realloc 两次无法在 Visual Studio 上运行
- 使用 getline(cin, var) 两次在进行字符串比较时会产生错误 (==)
- 为什么映射插入和 map.find() 的单次迭代比插入和 map.find() 的两次单独迭代慢得多
- C++析构函数调用两次,堆栈分配的复合对象
- 为什么参数在构造 std::thread 时移动两次
- Qt插槽调用了两次
- 做 std::用相同的unique_ptr移动两次
- C++两次定义相同的函数会导致错误
- 为什么具有静态存储持续时间的同一内联变量在包含在 VS2017 编译的两个翻译单元中时会构造和销毁两次
- 对于优化级别为 0 的 std::vector,析构函数被调用两次
- 使用柯南打包时如何避免列出两次依赖?
- 为什么要执行两次位移((x >> 4)<< 4)?
- 对结构向量进行两次排序
- 如果我使用同一个密钥推送用户数据两次,会发生什么
- 插入 std::unordered_map 在 MSVC++ 的 STL 中调用哈希函数两次,设计不好还是特殊原因?