Forward Iterator C++ : for_each behavior

吃可爱长大的小学妹 提交于 2019-12-11 19:52:28

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!