George Kourtis
George Kourtis

Reputation: 2592

How to define iterator for a special case in order to use it in a for loop using the auto keyword

I would like to define the subsequent code in order to be able to use it like "for (auto x:c0){ printf("%i ",x); }"

But I do not understand something and i have searched it for some time.

The error I get is: error: invalid type argument of unary ‘*’ (have ‘CC::iterator {aka int}’)

#include <stdio.h>

class CC{
  int a[0x20];
  public: typedef int iterator;
  public: iterator begin(){return (iterator)0;}
  public: iterator end(){return (iterator)0x20;}
  public: int& operator*(iterator i){return this->a[(int)i];}
} ;

int main(int argc, char **argv)
{ class CC c0;
  for (auto x:c0){
    printf("%i ",x);
  }
  printf("\n");
  return 0;
}

Upvotes: 0

Views: 110

Answers (2)

Alexey Kukanov
Alexey Kukanov

Reputation: 12784

Dietmar Kühl explained well why your code does not work: you cannot make int behaving as an iterator.

For the given case, a suitable iterator can be defined as a pointer to int. The following code is tested at ideone:

#include <stdio.h>

class CC{
  int a[0x20];
  public: typedef int* iterator;
  public: iterator begin() {return a;}
  public: iterator end() {return a+0x20;}
} ;

int main(int argc, char **argv)
{
  class CC c0;
  int i = 0;
  for (auto& x:c0){
    x = ++i;
  }
  for (auto x:c0){
    printf("%i ",x);
  }
  printf("\n");
  return 0;
}

Upvotes: 1

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 153955

It seems you are trying to use int as you iterator type using the member operator*() as the deference operations. That won't work:

  1. The operator*() you defined is a binary operator (multiplication) rather than a unary dereference operation.
  2. You can't overload operators for built-in types and an iterator type needs to have a dereference operator.

To be able to use the range-based for you'll need to create a forward iterator type which needs a couple of operations:

  1. Life-time management, i.e., copy constructor, copy assignment, and destruction (typically the generated ones are sufficient).
  2. Positioning, i.e., operator++() and operator++(int).
  3. Value access, i.e., operator*() and potentially operator->().
  4. Validity check, i.e., operator==() and operator!=().

Something like this should be sufficient:

class custom_iterator {
    int* array;
    int  index;
public:
    typedef int         value_type;
    typedef std::size_t size_type;
    custom_iterator(int* array, int index): array(array), index(index) {}
    int& operator*()             { return this->array[this->index]; }
    int const& operator*() const { return this->array[this->index]; }
    custom_iterator& operator++() {
        ++this->index;
        return *this;
    }
    custom_iterator  operator++(int) {
        custom_iterator rc(*this);
        this->operator++();
        return rc;
    }
    bool operator== (custom_iterator const& other) const {
        return this->index = other.index;
    }
    bool operator!= (custom_iteartor const& other) const {
        return !(*this == other);
    }
};

You begin() and end() methods would then return a suitably constructed version of this iterator. You may want to hook the iterator up with suitable std::iterator_traits<...> but I don't think these are required for use with range-based for.

Upvotes: 3

Related Questions