问题
So we know that foreach is something like :
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f)
{
for ( ; first!=last; ++first ) f(*first);
return f;
}
I have implemented a
template <typename T>
class Range
The problem is that when I use this function with the for_Each :
static void add1(float &v)
{
++v;
}
it go in infinite loop because of first "!=" last (it's not first"<"last), so how people do when they implemente their own forward iterator to work with for_each ?
回答1:
The problem of your approach is that your iterator increment operators do not change the iterator, but rather the stored value. This means that inside the for_each
loop, the condition is modified both in the increment operator of the iterator and also through the function.
回答2:
What are you trying to achieve anyway... does your class have any internal storage at all, or is it only represenatation of number sequence?
If so, then the problem are operators T &operator*()
and T *operator->()
which allow value pointed to by iterator to be changed. But if it's only range class it obviously cannot be edited this way, and you should rather use cons_iterator
.
If your class have an internal storage then your interator should have pointer to it (T* pData_;
instead of T pData_;
) and then you dont need to remove star in T &operator*()
which suprisingly was ready for correct version ;) The point is that incrementivg iterator should increment this pointer and incrementing value in your add1
function should increment pointed value. Now both operations increment the same variable, so you get those +=2
.
EDIT:
@DavidRodríguez-dribeas - You are considering different input iterator categories, which are meant to describe the difference between allowed sequential access (in what sequence elements can be accessed), but described problem is rather that Range
class instances are by design constant (at least not possible to be modified using just iterators). I can't see anything against giving it even random access category.
The only option is to replace iterator with const_iterator everywhere, so that iterated values cannot be modified. Now this is a bit against ISO C++, which divides containers into several categories and for each category it requires begin()
and end()
to return const_iterator
only if container itself is declared const
. However I think it is arguable at most that Range
class is even container in ISO C++ view, as (according to 23.1.1)
Containers are objects that store other objects. They control allocation and deallocation of these objects through constructors, destructors, insert and erase operations.
Sice there seem not to be any actual storage of iterated objects in Range class, I think we are out-in-the-wild here, and don't need to comply with standard that much.
EDIT2:
Firstly const iterator
is NOT the same as const_iterator
. The first one is an iterator that cannot be changed (to point to a different value in sequence eg. incremented), and another one allows that, but instead doesn't allow changing pointed value, so (*i)=3
would fail.
Secondly this is how I would have done it:
template <typename T>
class Range {
public:
class const_iterator;
typedef const_iterator iterator; //For stl-compliance - some algorythms use that.
IntegerRange(T low, T high) : low_(low), high_(high) { assert(low <= high);}
const_iterator begin() const { return const_iterator(low_); }
const_iterator end() const { return const_iterator(high_); }
private:
const T low_;
const T high_;
};
template<typename T>
class Range<T>::const_iterator : public std::iterator<std::forward_iterator_tag, T>
{
public:
// default constructor
const_iterator() : pData_(0)
{
}
// constructor from const T&
const_iterator(const T &pData) : pData_(pData)
{
}
// constructor from T&
const_iterator(T &pData) : pData_(pData)
{
}
// operator =
const_iterator &operator=(const const_iterator &other)
{
this->pData_ = other.pData_;
return *this;
}
// pre-increment operator
const_iterator & operator++()
{
++(this->pData_);
return *this;
}
// post-increment operator
const_iterator operator++(int)
{
const_iterator temp(*this);
this->operator++();
return temp;
}
// operator ==
bool operator==(const const_iterator &other) const
{
return this->pData_ == other.pData_;
}
// operator !=
bool operator!=(const iterator &other) const
{
return !operator==(other);
}
// operator* r-value
const T &operator*() const
{
return (this->pData_); // had to remove the *
}
// operator-> r-value
const T *operator->() const
{
return &(this->pData_);
}
private:
T pData_;
};
You are obviously free to keep the name iterator instead of const_iterator (or use qqwfmnghng as class name) as long as you do keep to const_iterator specification - in this case not providing l-value operators, as those are intended to modify pointed value - what sense would that make?
回答3:
#include <assert.h> // assert
#include <iterator> // std::forward_iterator
#include <utility> // std::pair
#include <stddef.h> // ptrdiff_t, size_t
typedef size_t UnsignedSize;
typedef ptrdiff_t Size;
typedef Size Index;
template< class TpValue >
class ValueRange
{
public:
typedef TpValue Value;
private:
Value first_;
Size span_;
public:
class It
: public std::iterator< std::forward_iterator_tag, Value >
{
friend class ValueRange;
private:
Value first_;
Index i_;
It( Value const first, Index const i )
: first_( first )
, i_( i )
{}
public:
void operator++() { ++i_; }
Value operator*() const { return first_ + i_; }
bool operator!=( It const& other ) const
{ assert( first_ == other.first_ ); return (i_ != other.i_); }
bool operator<( It const& other ) const
{ assert( first_ == other.first_ ); return (i_ < other.i_); }
};
Value first() const { return first_; }
Value last() const { return first_ + span_; }
It begin() const { return It( first_, 0 ); }
It end() const { return It( first_, span_ + 1 ); }
ValueRange( Value const first, Value const last )
: first_( first )
, span_( Size( last - first ) )
{
assert( first_ + span_ == last );
}
};
#include <algorithm> // std::for_each
#include <iostream> // std::wcout, std::endl
int main()
{
using namespace std;
ValueRange< double > const r( 1.0, 6.0 );
for_each( r.begin(), r.end(), []( double x )
{
using std::wcout; // For Visual C++ 10.0.
wcout << x << " ";
} );
wcout << endl;
}
来源:https://stackoverflow.com/questions/10770489/forward-iterator-c-for-each-behavior