1

似乎 VC10 (Visual Studio 2010) 下 unordered_set/map 的移动构造函数在被调用后将右侧置于未定义状态,导致其他操作(如“插入”)惨遭失败。移动赋值运算符似乎工作正常。不过,正常的集合/映射似乎在所有情况下都表现正确。此外,在 VC11 (Visual Studio 2012) 下一切似乎都运行良好。

这是 VC10 下 _Hash 实现的错误还是我遗漏了什么?提前感谢您的任何投入!

#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>

std::set<int> si0;
std::unordered_set<int> usi0;

std::map<int, int> mii0;
std::unordered_map<int, int> umii0;

int _tmain(int argc, _TCHAR* argv[])
{
    si0.insert(0);
    si0.insert(1);
    si0.insert(2);
    std::set<int> si( std::move(si0) ); // fine!
    si0.insert(666);

    usi0.insert(0);
    usi0.insert(1);
    usi0.insert(2);
    //std::unordered_set<int> usi( std::move(usi0) ); // this seems to put usi0 to an undefined state, which makes 'insert' below cry!
    std::unordered_set<int> usi; usi = std::move(usi0); // this works!
    usi0.insert(666);

    mii0[0] = 0;
    mii0[1] = 1;
    mii0[2] = 2;
    std::map<int, int> mii( std::move(mii0) ); // fine!
    mii0[666] = 666;

    umii0[0] = 0;
    umii0[1] = 1;
    umii0[2] = 2;
    //std::unordered_map<int, int> umii( std::move(umii0) ); // this seems to put umii0 to an undefined state, which makes 'insert' below cry!
    std::unordered_map<int, int> umii; umii = std::move(umii0); // this works!
    umii0[666] = 666;

    return 0;
}
4

1 回答 1

1

好吧,在深入研究了VC10下的_Hash类实现,并与VC11的比较后,我注意到VC10下的_Hash类的移动构造函数从未初始化它的'_Max_bucket_size'成员,然后逻辑将这个未初始化的值交换为右侧(移动的 _Hash 实例),使右侧处于未初始化状态。

如果移动构造函数的右侧确实是一个临时对象,这很好,但我的逻辑依赖于移动构造函数来“重置”右侧对象,然后将重建右侧对象。

移动分配版本的工作原理是它需要以某种方式正确构造左侧对象(在这种情况下,默认构造,正确初始化'_Max_bucket_size'),并且当将其交换到右侧时,右侧不是处于糟糕的状态。然而,让右侧从左侧获取交换状态不符合移动赋值运算符的标准行为,它应该重置右侧,这不仅仅是纯粹的交换!

VC11 中的相应实现似乎已经纠正了该行为并符合标准。

于 2012-10-08T19:06:58.747 回答