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.
精彩评论