Neue for-schleife mit C++11 - Iterator immer synchron?



  • Beispiel:

    #include <vector>
    #include <iostream>
    
    int main()
    {
    	std::vector<int> v = { 1,2,3,4,5,6,7,8,9,10 };
    	auto it = v.begin();
    	for ( int i : v )
    	{
    		std::cout << "Element: " << i << std::endl;		
    		if ( it != v.end() ){
    			std::cout << "Iterator: " << *it << std::endl;
    			it++;
    		}
    	} 
    }
    

    Output:

    Shell schrieb:

    Element: 1
    Iterator: 1
    Element: 2
    Iterator: 2
    Element: 3
    Iterator: 3
    Element: 4
    Iterator: 4
    Element: 5
    Iterator: 5
    Element: 6
    Iterator: 6
    Element: 7
    Iterator: 7
    Element: 8
    Iterator: 8
    Element: 9
    Iterator: 9
    Element: 10
    Iterator: 10

    Ist das garantiert, dass der iterator immer synchron mit der for-loop ist in diesem Szenario? Oder ist diese Art der Schleife nur nützlich wenn ich keinen iterator brauche?



  • Ich denke man kann davon ausgehen. Ich kenn mich nicht wirklich im Standard aus, aber http://en.cppreference.com/w/cpp/language/range-for sagt, dass die Range based for in das hier umgewandelt wird:

    {
    
        auto && __range = range_expression ;
        for (auto __begin = begin_expr,
    
                    __end = end_expr;
    
                __begin != __end; ++__begin) {
    
            range_declaration = *__begin;
            loop_statement
    
        }
    
    }
    

    wobei begin_expr __range.begin() entspricht, wenn vorhanden.



  • Ok, danke.
    Hab noch die Frage hier gefunden:
    http://stackoverflow.com/questions/6953128/need-iterator-when-using-ranged-based-for-loops
    Dort wird das auch gesagt. Daher gehe ich mal davon aus, dass der iterator immer in sync ist. Es ist einfach schöner zu schreiben und flexibler als die "normale" for-loop. Nur löschen mitten in der loop wird so wohl nicht gehen, nehme ich mal an, da man den internen iterator nicht umbiegen kann. Wenn man nur 1 Element löschen will, dann kann man aber ja mit break; abbrechen. Und ansonsten muss halt die "normale" for loop herhalten.



  • Wenn du eine Iteror benötigst, ist doch

    #include <vector>
    #include <iostream>
    
    int main()
    {
        std::vector<int> v = { 1,2,3,4,5,6,7,8,9,10 };
        auto it = v.begin();
        for ( auto it = begin(v); it != end(v); ++it )
        {
            std::cout << "Iterator: " << *it << std::endl;
        }
    }
    

    viel übersichtlicher und bestimmt auch weniger Fehleranfällig.


  • Mod

    Ist das garantiert, dass der iterator immer synchron mit der for-loop ist in diesem Szenario?

    Jo, der von patrick zitierte Code ist in 6.5.4/1 so auch zu finden. Allerdings ist range-based for nicht dafuer konzipiert durch Iteratoren einer Range zu laufen.

    Man kann sich aber natuerlich einen Helfer schreiben. Runtergetippt (i.e. nicht ausfuehrlich getestet):

    template <typename Iter>
    class Wrapper {
        Iter _cur;
    
        using _traits_type = std::iterator_traits<Iter>;
    
    public:
    
        using iterator_type = Iter;
        using iterator_category = typename _traits_type::iterator_category;
        using value_type        = iterator_type;
        using difference_type   = typename _traits_type::difference_type;
        using pointer           = iterator_type*;
        using reference         = iterator_type&;
    
        iterator_type base() const { return _cur; }
    
        Wrapper() : _cur() {}
    
        explicit Wrapper(iterator_type i) : _cur(i) {}
    
        template <typename OtherIter>
        explicit Wrapper(Wrapper<OtherIter> const& i) : _cur(i.base()) {}
    
        iterator_type operator*() const {return _cur;}
        value_type const* operator->() const {return &_cur;}
    
        Wrapper& operator++() {++_cur; return *this;}
        Wrapper  operator++(int) {auto tmp = *this; ++*this; return tmp;}
        Wrapper& operator--() {--_cur; return *this;}
        Wrapper  operator--(int) {auto tmp = *this; --*this; return tmp;}
    
        Wrapper operator+(difference_type n) const {return {_cur + n};}
        Wrapper operator-(difference_type n) const {return {_cur - n};}
    
        Wrapper operator+=(difference_type n) const {return *this = *this + n;}
        Wrapper operator-=(difference_type n) const {return *this = *this - n;}
    
        iterator_type operator[](difference_type n) const { return _cur + n; }
    };
    
    template <typename I1, typename I2>
    bool operator==( Wrapper<I1> const& lhs, Wrapper<I2> const& rhs)
    { return lhs.base() == rhs.base(); }
    template <typename I1, typename I2>
    bool operator!=( Wrapper<I1> const& lhs, Wrapper<I2> const& rhs)
    { return !(lhs == rhs); }
    template <typename I1, typename I2>
    bool operator<(Wrapper<I1> const& lhs, Wrapper<I2> const& rhs)
    { return lhs.base() < rhs.base(); }
    template <typename I1, typename I2>
    bool operator<=(Wrapper<I1> const& lhs, Wrapper<I2> const& rhs)
    { return !(rhs < lhs); }
    template <typename I1, typename I2>
    bool operator>(Wrapper<I1> const& lhs, Wrapper<I2> const& rhs)
    { return rhs < lhs; }
    template <typename I1, typename I2>
    bool operator>=(Wrapper<I1> const& lhs, Wrapper<I2> const& rhs)
    { return !(lhs < rhs); }
    
    //! LWG #685
    template <typename I1, typename I2>
    auto operator-(Wrapper<I1> const& lhs, Wrapper<I2> const& rhs) -> decltype(lhs.base() - rhs.base())
    { return lhs.base() - rhs.base(); }
    template <typename I>
    Wrapper<I> operator+(typename Wrapper<I>::difference_type n, Wrapper<I> const& lhs)
    { return lhs + n; }
    template <typename I>
    Wrapper<I> makeWrapper(I i)
    { return {i}; }
    
    template <typename Iter>
    struct IteratorPair {Iter first, last;};
    
    template <typename Iter>
    Iter begin(IteratorPair<Iter> const& pair) {return pair.first;}
    template <typename Iter>
    Iter   end(IteratorPair<Iter> const& pair) {return pair.last;}
    
    template <typename Iter>
    IteratorPair<Wrapper<Iter>> iters(Iter first, Iter last) {
        return {Wrapper<Iter>(first), Wrapper<Iter>(last)};
    }
    
    // ADL darf jemand anders einbauen. detail-namespace errichten, using-declarationen dort reinpasten, usw.
    template <typename Range>
    auto iters(Range& r) -> decltype(iters(std::begin(r), std::end(r))) {
        return iters(std::begin(r), std::end(r));
    }
    

    Kann man bspw. so verwenden:

    std::vector<int> v = { 1,2,3,4,5,6,7,8,9,10 };
        for ( auto it : iters(v) )
            std::cout << "Element: " << *it << std::endl;
    

    Demo.

    Edit: Macht natuerlich keinen Sinn base() modifizierbar zu lassen.
    Edit²: Ups. Hätte wohl mit -std=c++11 kompilieren sollen, dann wäre mir der Flüchtigkeitsfehler gleich aufgefallen.


Anmelden zum Antworten