Ælex
Ælex

Reputation: 14839

std::unordered_set insert, get the position where item was inserted

Assume I have class MyClass

class MyClass
{
public:

    MyClass( std::string str ) : _str(str) {}
    void SetPosition ( int i ) { _pos = i; }
    std::string _str;
    int _pos;
};

namespace std
{
    template<> struct hash<shared_ptr<MyClass>>
    {
        size_t operator()( const shared_ptr<MyClass> & ptr ) const
        {
            return hash<string>()( ptr->_str ) + hash<int>()( ptr->_pos );
        }
    };
}

When using std::vector, I was able to do this:

std::string str = "blah";
auto ptr = std::make_shared<MyClass>( str );
std::vector<std::shared_ptr<MyClass>> vector;
vector.push_back( ptr );
ptr->SetPosition ( std::addressof( vector.back() ) - std::addressof( vector[0] ) );
std::cout <<  ptr->_str <<  " is at " << ptr->_pos << std::endl;

In order to calculate where in the vector, my object pointer was placed.

However, If I want to use std::unordered_set (which I do), then:

std::string str = "blah";
auto ptr = std::make_shared<MyClass>( str );
std::unordered_set<std::shared_ptr<MyClass>> set;
auto res = set.insert( ptr );
ptr->SetPosition ( std::addressof( res.first ) - std::addressof( set[0] ) );
std::cout <<  ptr->_str <<  " is at " << ptr->_pos << std::endl;

Will not work. Neither will

std::addressof( set.begin() );

Nor will,

std::addressof( set.begin().first );

or any other way I try to use the front iterator.

  1. Does this make sense? Or should I rely on set.size() and assume that my pointer was inserted at the end?
  2. Is there any way to safely get the position where that pointer was inserted using something similar to the above code?

Upvotes: 0

Views: 1717

Answers (1)

Barry
Barry

Reputation: 303147

unordered_set, like the name implies, is unordered. You can keep track of the position of your elements in a vector, because as long as you don't erase anything, they won't change places. But that's not true of unordered_set. For instance, on my implementation, here's what printing all the elements in order after every insert would yield:

std::unordered_set<int> s;
s.insert(0); // 0
s.insert(1); // 1 0
s.insert(2); // 2 1 0
s.insert(3); // 3 2 1 0
...
s.insert(22); // 22 0 1 2 3 ... 19 20 21
...
s.insert(48); // 48 47 46 45 ... 22 0 1 2 3 4 ... 21

So what I'm trying to say is order is definitely not something that makes sense for you to rely on.

With your vector, however, you can do much better in terms of setting position:

vector.push_back(ptr);
ptr->SetPosition(vector.size() - 1);    

Upvotes: 3

Related Questions