It seems that the move constructors of unordered_set/map under VC10 (Visual Studio 2010) puts the right hand side to an undefined state after being invoked, causing other operations (such as 'insert') to fail miserably. The move assignment operators seem to work fine. The normal set/map seem to behave correctly under all cases though. Furthermore, everything seems to be working fine under VC11 (Visual Studio 2012).

Is this a bug on the _Hash implementation under VC10 or I am missing anything? Thanks in advance for any inputs!

#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;
}
有帮助吗?

解决方案

Alrighty, after digging more into the _Hash class implementation under VC10, and comparing it to that of VC11, I noticed that the move constructor of _Hash class under VC10 never initializes its '_Max_bucket_size' member, and the logic then swaps this uninitialized value to the right hand side (the moved _Hash instance), leaving the right hand side in an uninitialized state.

This is fine if the right hand side of the move constructor is really a temporary object, but my logic relies on the move constructor to "reset" the right hand side object, and the right hand side object will be reconstructed thereafter.

The move assignment version works in that it requires the left hand side object to be somehow properly constructed (in this case, default constructed, with '_Max_bucket_size' properly initialized), and when swapping it to the right side, the right hand side is not left in a bad state. Nevertheless, leaving the right hand side taking the swapped state from the left hand side does not conform to the standard behavior of move assignment operator, which should reset the right hand side, which does something more than pure swapping!

The corresponding implementation in VC11 seems to have corrected the behavior and conform to the standard.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top