Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing STL like iterator

I was trying to learn writing stl like iterators, for that I wrote a simple circular array and added an iterator in it. Please look at the bottom of the code to see the problem.

template<typename T, int N>
class RingQueue{
    T * _marray;
    int _mbegin;
    int _msize;
public:
    RingQueue(){
        _marray = new T[N];
        _mbegin = 0;
        _msize= 0;
    }   
    void push_back(const T& val){
        if(_msize!=N){
            _marray[(_mbegin+_msize)%N] = val;
            _msize++;
        }
        else
            throw "Queue Full";
    }   
    T pop_front(){
        if(_msize!=0){
            T&val = _marray[_mbegin];
            _mbegin = (_mbegin+1)%N;
            _msize--;
            return val;
        }
        else
            throw "Queue Empty";
    }

    class iterator{
        RingQueue<T,N>* _container;
        int _idx;
        public:
        iterator(RingQueue<T,N>* container,int idx):_container(container){
            _idx = idx;
        }

        bool operator==(iterator &rhs){
            return (this->_container==rhs._container && this->_idx == rhs._idx);
        }
        bool operator!=(iterator &rhs){
            return !(*this==rhs);
        }   
        T operator*(){
            if(_container->_msize>0&&_idx<_container->_msize){
                return _container->_marray[(_container->_mbegin+_idx)%N];
            }
        }

        iterator& operator++(){
            if(_container->_msize ==0){
                *this = _container->end();
                return *this;
            }
            if(_idx==_container->_msize){   
                *this = _container->end();
                return *this;
            }
            _idx++;
            return *this;
        }
    };
    iterator begin(){
        return iterator(this,0);
    }
    iterator end(){
        return iterator(this,_msize);
    }
};
int current=0;
int gen(){
    return current++;
}

int curr_op=0;
int operation(){
    return 2*(curr_op++&1)-1;
}
int main(){
    RingQueue<int,10> ring;
    vector<int> v(9),op(9);
    generate(v.begin(),v.end(),gen);
    random_shuffle(v.begin(),v.end());
    copy(v.begin(),v.end(),ostream_iterator<int>(cout," "));    
    cout<<endl;

    generate(op.begin(),op.end(),operation);        
    random_shuffle(op.begin(),op.end());
    // copy(op.begin(),op.end(),ostream_iterator<int>(cout," "));   
    cout<<endl;

    for(vector<int>::iterator itv  = v.begin();itv!=v.end();itv++){
        try{
            ring.push_back(*itv);   
        }catch(const char * e){
            cout<<*itv<<e<<endl;
        }
    }
    //works
    RingQueue<int,10>::iterator ite = ring.end();
    for(RingQueue<int,10>::iterator it = ring.begin(); it!=ite; ++it){
        cout<<*it<<endl;
    }
    // doesn't work 
    for(RingQueue<int,10>::iterator it = ring.begin(); it!=ring.end(); ++it){
        cout<<*it<<endl;
    }
    return 0;
}

When I compile the doesn't work part, g++ dumps the following error

ringqueue.cpp: In function ‘int main()’:
ringqueue.cpp:112: error: no match for ‘operator!=’ in ‘it != ring.RingQueue<T, N>::end [with T = int, int N = 10]()’
ringqueue.cpp:48: note: candidates are: bool RingQueue<T, N>::iterator::operator!=(RingQueue<T, N>::iterator&) [with T = int, int N = 10]

the works part compiles seamlessly, when compiled without doesn't work part. Can somebody explain me what's wrong.

like image 648
HBY4PI Avatar asked Jan 17 '23 13:01

HBY4PI


1 Answers

I think the issue is in these lines:

    bool operator==(iterator &rhs){
        return (this->_container==rhs._container && this->_idx == rhs._idx);
    }
    bool operator!=(iterator &rhs){
        return !(*this==rhs);
    }  

The problem here is that these functions take in lvalue references to their arguments. This means that if you try passing an rvalue (for example, a temporary object returned from a function) into these operators, you will get a compile-time error because references cannot bind to temporaries. To fix this, either change the arguments to be const references:

    bool operator==(const iterator &rhs){
        return (this->_container==rhs._container && this->_idx == rhs._idx);
    }
    bool operator!=(const iterator &rhs){
        return !(*this==rhs);
    }  

Since const references can bind to temporaries, or have them take their arguments by value:

    bool operator==(iterator rhs){
        return (this->_container==rhs._container && this->_idx == rhs._idx);
    }
    bool operator!=(iterator rhs){
        return !(*this==rhs);
    }  

Whichever choice you make, you should probably mark these functions const because they don't mutate the receiver object.

Hope this helps!

like image 112
templatetypedef Avatar answered Jan 22 '23 18:01

templatetypedef