I have a data structure that is essentially a hash table. The keys and values are stored separately for cache reasons. I have an iterator that traverses the container and returns pairs of keys and values when dereferenced.
However, I'm having a bit of trouble getting the iterator to behave like others. In particular with operator->
. Here's what I have so far:
struct KeyValuePair
{
KeyValuePair(const int& key, int& value) : key(key), value(value) {}
const int& key;
int& value;
};
struct Iterator
{
KeyValuePair operator*() { return KeyValuePair(*keys, *values); }
// TODO: Implement this
//KeyValuePair* operator->() { return ... }
int* keys = nullptr;
int* values = nullptr;
};
This works fine for range-for and explicitly dereferencing the iterator
auto it = container.begin();
KeyValuePair pair = *it;
But it doesn't work for 'reaching through' the iterator because I don't have operator->
auto it = container.begin();
int& value = it->value;
And I can't figure out how to implement operator->
for this iterator. My first thought was to stick a KeyValuePair
in the iterator and return a pointer to that, but a reference can't be reseated without shenanigans.
Any hints from folks more clever than myself?
If you can't return a pointer from the operator->
, return a helper class by value. Make that class store a KeyValuePair
and overload its operator->
to return a pointer to the pair.
My answer uses the same idea as RedFog's one, but I tried to make the code less convoluted.
struct Iterator
{
KeyValuePair operator*() const
{
return KeyValuePair(*keys, *values);
}
class ArrowHelper
{
KeyValuePair value
public:
ArrowHelper(KeyValuePair value) : value(value) {}
KeyValuePair *operator->() const
{
return &value;
}
};
ArrowHelper operator->() const
{
return **this;
}
int* keys = nullptr;
int* values = nullptr;
};
iterator sometimes return a temporary object as a "reference", but operator->
usually demands a lvalue to get its pointer. in this case, to let operator->
work, we should provide a wrapper of the pointer: (C++11 version)
struct KeyValuePair
{
KeyValuePair(const int& key, int& value) : key(key), value(value) {}
const int& key;
int& value;
private:
template<typename T>
struct Helper{
T ref;
T* operator->(){ return std::addressof(ref); }
};
Helper<KeyValuePair> get(){ return Helper<KeyValuePair>{*this}; }
friend struct Iterator;
};
struct Iterator
{
KeyValuePair operator*() { return KeyValuePair(*keys, *values); }
auto operator->()->decltype((*(*this)).get()){
return (*(*this)).get();
}
int* keys = nullptr;
int* values = nullptr;
};
it works if the "reference" object (KeyValuePair
) is copy constructible. the copy at the constructor of Helper
is necessary, because the temporary KeyValuePair
will be destructed after returning the wrapper.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With