operator-> for an iterator that returns a temporary

Viewed 263

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?

2 Answers

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.

Related