Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

operator-> for an iterator that returns a temporary

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?

like image 772
Adam Avatar asked Sep 01 '25 20:09

Adam


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;
};
like image 78
HolyBlackCat Avatar answered Sep 05 '25 03:09

HolyBlackCat


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.

like image 21
RedFog Avatar answered Sep 05 '25 03:09

RedFog