7

I'm writing a 3D grid for my scientific software and I need to iterate through the nodes of the grid to get their coordinates. Instead of holding each node object in the container I'd rather like to just calculate the coordinates on the fly while iterating. The problem is that stl::iterator requires to return reference to a value as a result of operator*(), or pointer for operator->().

Some of the code below:


class spGridIterator {
public:
    typedef forward_iterator_tag iterator_category;
    typedef spVector3D value_type;
    typedef int difference_type;
    typedef spVector3D* pointer;
    typedef spVector3D& reference;

    spGridIterator(spGrid* gr, int index);

    spGridIterator& operator++();
    spGridIterator& operator++(int);

    reference operator*() const;
    pointer operator->() const;

private:
    spGrid* m_grid;
    int m_idx;
};

spGridIterator::reference spGridIterator::operator*() const {
    // return m_grid->GetPoint(m_idx);
}

spGridIterator::pointer spGridIterator::operator->() const {
    // return m_grid->GetPoint(m_idx);
}

This method queries the node coordinates by index provided


spVector3D spGrid::GetPoint(int idx) const {
    // spVector3D vec = ... calculate the coordinates here ...
    return vec;
}

Any input on this?

Thanks in advance, Ilya

ezpresso
  • 7,896
  • 13
  • 62
  • 94
  • "The problem is that stl::iterator requires to return reference to a value as a result of operator*(), or pointer for operator->()" Why is that a problem? For what? – Cheers and hth. - Alf Oct 29 '10 at 09:07
  • I'm not sure I actually understand what you're trying to achieve. Perhaps you could include an example of client code to should how you're trying to use this collection. – Dave Hillier Oct 29 '10 at 09:13
  • "Why is that a problem? For what?" Because this way I forced to keep an actual variable to point (or reference) to. – ezpresso Oct 29 '10 at 11:11
  • I need iterator to dereference to a temporary variable, because the node coordinate should be calculated on the fly rather stored somewhere. – ezpresso Oct 29 '10 at 11:15

4 Answers4

7

You could use a member variable to hold the grid point it is currently pointing to:

class spGridIterator {
public:
    typedef forward_iterator_tag iterator_category;
    typedef spVector3D value_type;
    typedef int difference_type;
    typedef spVector3D* pointer;
    typedef const spVector3D* const_pointer;
    typedef const spVector3D& const_reference;
    typedef spVector3D& reference;

    spGridIterator(spGrid* gr, int index);

    spGridIterator& operator++();
    spGridIterator& operator++(int);

    reference operator*();
    const_reference operator*() const;

    pointer operator->();
    const_pointer operator->() const;

private:
    spGrid* m_grid;
    int m_idx;
    mutable spVector3D CurrentPoint;
};

Then the dereference operator could look like this:

spGridIterator::const_reference spGridIterator::operator*() const {
    CurrentPoint = m_grid->GetPoint(m_idx);
    return CurrentPoint;
}

Thanks to @greg for pointing out that CurrentPoint needs to be mutable for this to work. This would be a lazy implementation (only retrieving the point when the iterator is actually dereferenced). An eager implementation would update the CurrentPoint member in the mutator methods of the iterator (the operator++ variants in this example), making the mutable superfluous.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
  • Unless `CurrentPoint` is `mutable`, `operator*()` can't be `const`, correct? – greg Feb 12 '13 at 18:03
  • That is correct, this doesn't make sense. I fixed the answer. – Björn Pollex Feb 12 '13 at 20:09
  • This still isn't quite right, because you can't change the state of the `spGridIterator` object from within a `const` method. The assignment of `CurrentPoint` in `operator*()` won't compile unless `CurrentPoint` is `mutable`. – greg Feb 13 '13 at 13:14
  • 1
    Correct, I really shouldn't write code without a compiler. I fixed it now and added some explanation. Hope it is correct now, thanks for the input. – Björn Pollex Feb 13 '13 at 13:33
  • 1
    Thanks for the update. Just one note about doing the assignment in `operator++()`, etc. is that I don't think you'd be able to have a `const spGridIterator` (`const_iterator` type off of the container) without the `mutable`. – greg Feb 13 '13 at 15:14
2

I know this post is too old but just because I had the same "problems" and google brought me here, I will add my two cents, here is what I found:

At least in the C++ library there are many iterator types, each one declaring some related semantics. The types are

  • Input iterators
  • Forward iterators
  • Bidirectional iterators
  • Random access iterators
  • Output iterators

In your problem case the input iterators semantics fit. Specifically in input iterators operator*() does not have to return a reference to an object it can even return the a newly created object. Thus avoiding having an "dummy" object inside the iterator object as Bjorn suggested.

You can view more here.

user183833
  • 228
  • 1
  • 8
1

Since an iterator is a value object, why not just setting a member to the value you want to return, and return a reference to the member?

Jan
  • 1,807
  • 13
  • 26
1

Short answer is, this will lead to undefined behaviour, you are after all returning a reference to a temporary! One option (if this iterator does not have to re-entrant is to have a class member (of type spVector3D) to which you "assign" the returned value (of course you could do it more optimally by passing in a reference to this to GetPoint as well as index), and then return that.

Nim
  • 33,299
  • 2
  • 62
  • 101