Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Semantic of -> operator in lists (and in general C++)

My current assignment is writing a list with iterators. The list isn't being a problem so much as creating the iterator class is.

From a couple of sources I've seen that I have two operators to define in my iterator class: operator* and operator->.

Great so far! Supposing my iterator structure is so

// Nested class of List
class _Iter
{
private:
    ListElem *pCurr;
    const List *pList;

public:
    _Iter(ListElem *pCurr, const List *list)
        : pCurr_(pCurr), pList(list)
    {}

    T& operator*() { return pCurr_->data; }
    T* operator->() { return &**this; }
};

with ListElem being

// Nested struct of List
struct ListElem
{
    T data;

    ListElem *next;
    ListElem *prev;
};

I can see I'm doing something massively wrong (as double dereferencing of this would lead to a &(*pCurr_->data), which is not dereferencable.

My main problem is not understanding what -> is actually supposed to do in this case. Should it grant the user access to the ListElem class? If that's the case, why can't I just write

ListElem *operator->() { return pCurr_; }

instead of returning a pointer? My understanding of these two operators as used in my list (and hopefully STL lists) is that:

operator*() // Return data pointed to by iterator; pCurr_->data;
operator->() // Grant access to data-holding structure; pCurr; 

Is this correct, or what am I not getting? (And does -> have a proper name?)

like image 359
IAE Avatar asked Dec 19 '10 15:12

IAE


3 Answers

Whatever you do, (*something).somethingElse should be equivalent to something->somethingElse. The latter is just a short syntax for the former. Therefore,

T& operator*() { return pCurr_->data; }
T* operator->() { return &**this; }

is fine because *this just dereferences this which has the type _Iter*, not _Iter, so no operator*() call is done. Then you dereference *this, so you get pCurr->data, then you take its address, so you get &pCurr->data. But it would be much clearer to just write:

T& operator*() { return pCurr_->data; }
T* operator->() { return &pCurr->data; }

Now, this

ListElem *operator->() { return pCurr_; }

is wrong because if operator*() returns T&, operator->() should return T*, that's what it was designed for. If you really want to grant access to ListItem instead of its data (which may or may not make sense depending on the design, but in your case it looks like it doesn't), then you should also redefine operator*() to get this:

ListElem& operator*() { return *pCurr_; }
ListElem *operator->() { return pCurr_; }

Note that it is not a language requirement, it is just how you design your class to avoid confusing interface.

like image 122
Sergei Tachenov Avatar answered Oct 27 '22 04:10

Sergei Tachenov


Your main guideline should be that

(*iter).hello();
iter->hello();

should both do the same thing. That's what the user expects. Returning ListElem gives nothing to the user. The user shouldn't even know the details of the implementation of ListElem.

like image 1
Yakov Galka Avatar answered Oct 27 '22 02:10

Yakov Galka


operator-> gives a pointer to the object pointed to by the iterator, in this case (apparently) pCurr_->data.

T *operator->() { return &(pCurr_->data); }

It should return the address of the object returned by operator*() as a value or reference.

T &operator*() { return pCurr_->data; }
// or
T &operator*() { return *operator->(); }

operator->() exists to implement -> with iterators (with the behavior it has for pointers) and is necessary because operator* may return an object by value instead of by reference.

Note that you don't need to store a pointer to the List in the iterator to obtain the functionality required.

like image 1
Fred Foo Avatar answered Oct 27 '22 02:10

Fred Foo