开发者

Using boost_foreach without const_iterator

Is there a way to use boost foreach without defining a const_iterator?

My use-case for this is an iterator for a vector, that can contain invalid elements. The iterator should traverse the vector, and yield only the valid elements. It should a开发者_运维知识库lso repair the vector in the sense that it should swap each invalid item with the next valid item and resize the vector at the end. For example if -1 represents invalid values the vector [6,-1,-1,9,-1,2] should iterate over 6,9 and 2 and leave the vector as [6,9,2].

I tried implementing this with boost::iterator_facade but I could not think of a way to implement a const_iterator, because the vector can change by removing invalid values and thus cannot be const.


Separation of concerns: the container is responsible for its invariants, the iterators for traversal. If you move the repairing to the container you can separate the logical const from the mutable, hidden parts.

Can you write your iterators the 'dumbest' way possible to disentangle them from the container? For instance storing a numerical index (if it makes sense for your container), then calling a private friend (or more) of the container to access the logical n-th element.

The private friend(s) can then be overloaded on const and may still modify the mutable parts to do the repairing you describe, and then return the element.


An (abridged) example of container supporting random-access (and thus numerical indices for access):

template<typename T>
class vector {
    mutable std::vector<std::weak_ptr<T>> data; // notice mutable

    T&
    fetch(int n);

    T const&
    fetch(int n) const; // notice const overload

public:
    class const_iterator;
    friend class const_iterator;

    const_iterator
    begin() const;
};

template<typename T>
class vector<T>::const_iterator {
    int index;
    vector<T> const* this_; // notice const
public:
    // constructors go here

    const_iterator&
    operator++()
    { ++index; }
    // ...

    T const&
    operator*() const
    { return this_->fetch(index); } // this will call the const version of fetch
};

// example implementation of the const version of fetch
template<typename T>
T const&
vector<T>::fetch(int n) const
{
    auto removed = std::remove_if(data.begin(), data.end(), [](std::weak_ptr<T>& element)
    { return element.expired(); });
    // mutate mutable data member in a logically const member
    data.erase(data.begin(), removed);

    // this assumes that there is no race condition
    // bear with me for the sake of understanding the mutable keyword
    return *data[n].lock();
}


All forms of "foreach" are specifically for iterating over every element of a container. You are not just iterating over every element of a container. You are modifying the container as you iterate.

So just write a regular for-loop. There's no need for special cleverness or anything.


Here's the code for it:

std::vector<int> it = vec.begin();
for(; it != vec.end;)
{
  if(*it < 0)
  {
    it = vec.erase(it);
    continue;
  }
  else
  {
    //Do stuff with `it`.
    ++it;
  }
}

See, just a simple loop. No need for fancy iterator facades or other such gimmickry.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜