c++inheritanceoperator-overloadingranged-loops

what the "range-based for" call of iteration


I am recently trying to know how ranged-for work, and finally got a picture as below:

for(obj& temp_ref:range r)//hide iterator somewhere as the range defined.

For each iteration,there is just a update of current object to doing the job in one circle of loop,and this is decent explain for me.
   
 
But when I trying to combine range and iterator into a mixed class,
there`s a problem of which operator++ should it call in my code,
in the sense, it should be the derivative class method be call in range-based-for,
because I create the object as derivative object,my declare is virtual.

the code is provided here,the first part is sprated and fine, but the second one is works weird for me.

#include <iostream>

namespace first
{
        template<class iterator>
        class range
        {
            public:
                range(iterator ina,iterator inb):a(ina),b(inb){};
                iterator a,b;
                iterator& begin(){return a;};
                iterator& end(){return b;};
        };
        template<class T>
        class iterator
        {
            public:
                iterator(T* ini):i(ini){};
                T* i;
                virtual iterator<T>& operator= (const iterator<T>& other){this->i=other.i;return *this;};
                virtual T& operator* (){return *i;};
                virtual T operator!= (const iterator<T>& other){return this->i==other.i?0:1;};
                virtual void operator++ (){i++;};
        };
        class jump2:public iterator<int>
        {
            public:
                jump2(int* ini):iterator<int>(ini){};
                virtual void operator++ (){i+=2;};
        };

}

namespace second
{
    template<class T>
    class iterator
    {

        public:
            iterator(T* inStart,T* inFinal):current(inStart),final(inFinal){};
            T* current;
            T* final;
            iterator<T> begin(){return iterator<T>(this->current,this->final);};
            iterator<T> end(){return iterator<T>(this->final,this->final);};
            virtual iterator<T>& operator= (const iterator<T>& other){this->current=other.current;this->final=other.final;return *this;};
            virtual T& operator* (){return *this->current;};
            virtual bool operator!= (iterator<T>& other){return this->current!=other.final;};



            virtual void operator++ ()
            {
                std::cout<<"<call base>";
                this->current=this->current+1;
            };



    };
    template<class T>
    class jumper:public iterator<T>
    {
        public:

            jumper(T* inStart,T* inFinal):iterator<T>(inStart,inFinal){};



            void operator++ ()
            {
                std::cout<<"<call deri>";
                this->current=this->current+2;
            };



    };
};

int main()
{

    int a[6]={1,0,2,0,3,0};

    //success
    {
        using namespace first;

        range<jump2> a_range(jump2(a),jump2(a+6));


        for(int store:a_range)
            std::cout<<store<<std::endl;
    }//pause();


    //Fail
    {
        using namespace second;

        jumper<int> a_iterator_call_jumper(a,a+6);

        for(int& i:a_iterator_call_jumper)
            std::cout<<i<<std::endl;

    }//pause();



    return 0;
};

this output is

1
2
3
1
<call base>0
<call base>2
<call base>0
<call base>3
<call base>0
<call base>

but it should be like

1
2
3
1
<call deri>2
<call deri>3
<call deri>

Is this thing goes wrong because I use it wrong?
Or there`s a bug I am not find yet?


Solution

  •     template<class iterator>
        class range
        {
            public:
                range(iterator ina,iterator inb):a(ina),b(inb){};
                iterator a,b;
                iterator& begin(){return a;};
                iterator& end(){return b;};
        };
    

    no

        template<class iterator>
        class range
        {
            public:
                range(iterator ina,iterator inb):a(ina),b(inb){};
                iterator a,b;
                iterator begin() const {return a;};
                iterator end() const {return b;};
        };
    

    yes.

        template<class T>
        class iterator
        {
            public:
                iterator(T* ini):i(ini){};
                T* i;
                virtual iterator<T>& operator= (const iterator<T>& other){this->i=other.i;return *this;};
                virtual T& operator* (){return *i;};
                virtual T operator!= (const iterator<T>& other){return this->i==other.i?0:1;};
                virtual void operator++ (){i++;};
        };
    

    no

        template<class T>
        class iterator
        {
            public:
                iterator(T* ini):i(ini){};
                T* i;
                iterator( iterator const& ) = default;
                iterator& operator= (const iterator<T>& other) & = default;
                T& operator*() {return *i;};
                bool operator!= (const iterator& other) const {return this->i==other.i?0:1;};
                void operator++() {++i;};
        };
    

    yes.

    Don't use the standard C++ vtable-based polymorphic object model for iterators; they should be regular types, and the vtable-based polymorphic object model is not regular.

    As for second, it is not a good plan. An iterator and the range over which you iterate are not the same thing. Smashing them together makes your class incoherent.

    C++ iteration is not designed for dynamic dispatch (polymorphism). You can do it, but it kills performance, and it isn't trivial to write. You basically need to hide the virtual dispatch behind a regular type (and you can dispatch using vtables, or you can dispatch using C++'s void pointer based type erasure).

    If you are used to other languages, many other languages descended from C++ use mandatory reference semantics with garbage collection for objects of class type. Their virtual inheritance object model works on reference variables, as it does in C++ with reference and pointer types.

    C++ is relatively quirky (in this day and age) in that it supports complex objects as value types, and that the language and standard library presumed you are working with value types most of the time. (Note that a pointer is a kind of value type; but its value is the pointer, not the object), and that when you want to use reference/pointer semantics you have to adapt your code.