2010-07-06 8 views
7

Sto provando a convertire una classe iteratore Devo essere compatibile con stl in modo che possa essere utilizzata con gli algoritmi di stl. Nel seguente esempio semplice (e francamente inutile), che dovrebbe stampare i valori da 0 a 5 compreso, sto ottenendo i seguenti errori,Scrittura di iteratori compatibili con stl

ISO C++ vieta incrementando un puntatore di tipo 'Iterator (*)()'

e,

valida conversione da 'Iterator (*)()' a 'int'

che cosa sto facendo di sbagliato?

Grazie.


#include <iterator> 
#include <algorithm> 
#include <iostream> 

class Iterator : public std::iterator<std::bidirectional_iterator_tag, int> { 
    public: 
     Iterator(int i = 0) : val(i) { 
      if(val<0 || val>5) throw; 
     } 

     bool operator==(Iterator const& rhs) const { 
      return (val==rhs.val); 
     } 

     bool operator!=(Iterator const& rhs) const { 
      return !(*this==rhs); 
     } 

     Iterator& operator++() { 
      if(val!=6) 
       ++val; 
      return *this; 
     } 

     Iterator operator++(int) { 
      Iterator tmp (*this); 
      ++(*this); 
      return tmp; 
     } 

     Iterator& operator--() { 
      if(val!=-1) 
       --val; 
      return *this; 
     } 

     Iterator operator--(int) { 
      Iterator tmp (*this); 
      --(*this); 
      return tmp; 
     } 

     int operator*() const { 
      if(val==-1 || val==6) throw; 
      return val; 
     } 

    private: 
     int val; 
}; 

Iterator begin() { 
    return Iterator(); 
} 

Iterator end() { 
    return ++Iterator(5); 
} 

void print(int i) { 
    std::cout << i << std::endl; 
} 

int main(int argc, char* argv[]) { 
    std::for_each(begin,end,print); 
} 
+0

Si sta utilizzando questo per una classe contenitore personalizzata? – Alerty

risposta

10

Si passa le funzioni begin e end a std::for_each, invece dei iteratori che queste funzioni sarebbero tornati:

std::for_each(begin,end,print); 

dovrebbe essere:

std::for_each(begin(),end(),print); 

anche si noti che il vuoto throw dichiarazioni, come in if(val==-1 || val==6) throw;, non faranno nulla di buono.per inviare qualcosa, come throw std::out_of_range("out of bounds").

+0

Potrebbe usare 'throw()' se vuole forzare la funzione a non lanciare nulla. Ad esempio, non vorrai che un distruttore di classe lanci qualcosa. Quindi ha una precauzione si potrebbe usare 'throw()'. – Alerty

+0

Oh gawd. Come ho fatto a non vederlo. Mancanza di sonno forse (prometto di non fare altre domande finché non ne avrò un po '!). Grazie mille. Mi sento un po 'stupido ora. – tjm

+0

@Antony: Ma le funzioni vogliono lanciare quando l'indice è fuori limite, quindi una specifica di eccezione 'throw()' non aiuta. Anche 'throw()' non implica alcuna verifica del tempo di compilazione, terminerà semplicemente il programma se la funzione capita di lanciare comunque. – sth

8

In primo luogo, si dovrebbe passare l'iteratori restituiti da begin() e la fine() non le funzioni stesse:

int main(int argc, char* argv[]) 
{ 
    std::for_each(begin(),end(),print); 
} 

In secondo luogo, sarebbe utile avere una classe Iterator su modelli:

template<class T> 
class Iterator : public std::iterator<std::bidirectional_iterator_tag, int> 
{ 
    public: 
     typedef T value_type; //notice this here :D 

     Iterator(value_type t = 0) : val(t) 
     { 
      if(val<0 || val>5) throw; //never hardcode something like that :S 
     } 

     bool operator==(Iterator const& rhs) const 
     { 
      return (val==rhs.val); 
     } 

     bool operator!=(Iterator const& rhs) const 
     { 
      return !(*this==rhs); 
     } 

     Iterator& operator++() 
     { 
      if(val!=6) //never hardcode something like that :S 
       ++val; 
      return *this; 
     } 

     Iterator operator++(value_type) 
     { 
      Iterator tmp (*this); 
      ++(*this); 
      return tmp; 
     } 

     Iterator& operator--() 
     { 
      if(val!=-1) //never hardcode something like that :S 
       --val; 
      return *this; 
     } 

     Iterator operator--(value_type) 
     { 
      Iterator tmp (*this); 
      --(*this); 
      return tmp; 
     } 

     value_type operator*() const 
     { 
      if(val==-1 || val==6) throw; //never hardcode something like that :S 
      return val; 
     } 

    private: 
     value_type val; 
}; 

In terzo luogo, non si può davvero desiderare di avere una classe di iteratore come tale. Ecco un esempio di cosa è possibile fare (nota una classe di iteratore è un po 'più bassa):

#include <algorithm> 

template<class T> 
class List 
{ 
public: 
    typedef T value_type; 
    typedef std::size_t size_type; 

private: 
    struct Knot 
    { 
     value_type val_; 
     Knot * next_; 
     Knot(const value_type &val) 
     :val_(val), next_(0) 
     {} 
    }; 
    Knot * head_; 
    size_type nelems_; 

public: 
    //Default constructor 
    List() throw() 
    :head_(0), nelems_(0) 
    {} 
    bool empty() const throw() 
    { return size() == 0; } 
    size_type size() const throw() 
    { return nelems_; } 

private: 
    Knot * last() throw() //could be done better 
    { 
     if(empty()) return 0; 
     Knot *p = head_; 
     while (p->next_) 
      p = p->next_; 
     return p; 
    } 

public: 
    void push_back(const value_type & val) 
    { 
     Knot *p = last(); 
     if(!p) 
      head_ = new Knot(val); 
     else 
      p->next_ = new Knot(val); 
     ++nelems_; 
    } 
    void clear() throw() 
    { 
     while(head_) 
     { 
      Knot *p = head_->next_; 
      delete head_; 
      head_ = p; 
     } 
     nelems_ = 0; 
    } 
    //Destructor: 
    ~List() throw() 
    { clear(); } 
    //Iterators: 
    class iterator 
    { 
     Knot * cur_; 
    public: 
     iterator(Knot *p) throw() 
     :cur_(p) 
     {} 
     bool operator==(const iterator & iter)const throw() 
     { return cur_ == iter.cur_; } 
     bool operator!=(const iterator & iter)const throw() 
     { return !(*this == iter); } 
     iterator & operator++() 
     { 
      cur_ = cur_->next_; 
      return *this; 
     } 
     iterator operator++(int) 
     { 
      iterator temp(*this); 
      operator++(); 
      return temp; 
     } 
     value_type & operator*()throw() 
     { return cur_->val_; } 
     value_type operator*() const 
     { return cur_->val_; } 
     value_type operator->() 
     { return cur_->val_; } 
     const value_type operator->() const 
     { return cur_->val_; } 
    }; 
    iterator begin() throw() 
    { return iterator(head_); } 
    iterator begin() const throw() 
    { return iterator(head_); } 
    iterator end() throw() 
    { return iterator(0); } 
    iterator end() const throw() 
    { return iterator(0); } 
    //Copy constructor: 
    List(const List & lst) 
    :head_(0), nelems_(0) 
    { 
     for(iterator i = lst.begin(); i != lst.end(); ++i) 
      push_back(*i); 
    } 
    void swap(List & lst) throw() 
    { 
     std::swap(head_, lst.head_); 
     std::swap(nelems_, lst.nelems_); 
    } 
    List & operator=(const List & lst) 
    { 
     List(lst).swap(*this); 
     return *this; 
    } 
    //Conversion constructor 
    template<class U> 
    List(const List<U> &lst) 
    :head_(0), nelems_(0) 
    { 
     for(typename List<U>::iterator iter = lst.begin(); iter != lst.end(); ++iter) 
      push_back(*iter); 
    } 
    template<class U> 
    List & operator=(const List<U> &lst) 
    { 
     List(lst).swap(*this); 
     return *this; 
    } 
    //Sequence constructor: 
    template<class Iter> 
    List(Iter first, Iter last) 
    :head_(0), nelems_(0) 
    { 
     for(;first!=last; ++first) 
      push_back(*first); 
    } 
}; 

#include <iostream> 
using std::cout; 
using std::endl; 

int main() 
{ 
    const char MAX_LIMIT = 127; 
    List<short> listA; 
    //... 
    List<char> listB = listA; //without the conversion constructor this would not go very far! 
    for(char i = 0; i < MAX_LIMIT; ++i) 
     listB.push_back(i); 
    for(List<char>::iterator iter = listB.begin(); iter != lstB.end(); ++iter) 
     cout << *iter << endl; 
} 
+0

Grazie. Sì, nel mondo reale, ho il mio iteratore come classe interiore di un contenitore basato su modelli. Il mio post era solo un esempio di lavoro minimo che riproduceva gli errori che potevo vedere nel mio esempio del mondo reale. Non volevo solo un iteratore per iterare su 0-> 5! Ma grazie per la tua risposta. – tjm