Döngüler için aralık tabanlı kullanırken yineleyici gerekir


85

Şu anda, bununla yalnızca aralık tabanlı döngüler yapabiliyorum:

for (auto& value : values)

Ama bazen referans yerine değere bir yineleyiciye ihtiyacım oluyor (nedeni ne olursa olsun). Değerleri karşılaştıran tüm vektörü gözden geçirmek zorunda kalmadan herhangi bir yöntem var mı?

Yanıtlar:


78

Eski fordöngüyü şu şekilde kullanın :

for (auto it = values.begin(); it != values.end();  ++it )
{
       auto & value = *it;
       //...
}

Bununla, valueyineleyici kadar siz de olursunuz it. Ne kullanmak istiyorsan onu kullan.


DÜZENLE:

Bunu önermememe rağmen, ancak aralık tabanlı fordöngü kullanmak istiyorsanız (evet, herhangi bir nedenle : D), o zaman bunu yapabilirsiniz:

 auto it = std::begin(values); //std::begin is a free function in C++11
 for (auto& value : values)
 {
     //Use value or it - whatever you need!
     //...
     ++it; //at the end OR make sure you do this in each iteration
 }

Bu yaklaşım verilen arıyor kaçınır valueberi valueve ither zaman senkronize.


Evet, yaptığım şey bu. Bunun yerine aralık tabanlı döngülerle bir çözüm olup olmadığını merak ediyordum
小 太郎

4
Eski for döngüsüyle ilk çözümün çok daha iyi olduğuna katılıyorum: P
小 太郎

@ 小 太郎: Ya da std::findihtiyacınız olan şey bir değer bulmaksa kullanabilirsiniz ... Eski güzel algoritmalar hala yeni standarttadır.
David Rodríguez - dribeas

1
@David: Vektörde kopyalar varsa ne olur? valueve itsenkronize olmayabilir. valueBir referans olduğunu unutmayın .
Nawaz

9
@Nawaz: Sanırım son cümleyi yanlış anladım. Bilinen bir nesneyi bulmak için aralığı temel aldığını düşündüm. BTW, tercih ++itetmek it++daha az yükü olabilir gibi (kodunuzda hem kullanımlar) Mümkün.
David Rodríguez - dribeas

15

Gizli yineleyiciyi kendi değişkeninize takma ad vererek açığa çıkarmanıza izin veren bir proxy sarmalayıcı sınıfı burada.

#include <memory>
#include <iterator>

/*  Only provides the bare minimum to support range-based for loops.
    Since the internal iterator of a range-based for is inaccessible,
    there is no point in more functionality here. */
template< typename iter >
struct range_iterator_reference_wrapper
    : std::reference_wrapper< iter > {
    iter &operator++() { return ++ this->get(); }
    decltype( * std::declval< iter >() ) operator*() { return * this->get(); }
    range_iterator_reference_wrapper( iter &in )
        : std::reference_wrapper< iter >( in ) {}
    friend bool operator!= ( range_iterator_reference_wrapper const &l,
                             range_iterator_reference_wrapper const &r )
        { return l.get() != r.get(); }
};

namespace unpolluted {
    /*  Cannot call unqualified free functions begin() and end() from 
        within a class with members begin() and end() without this hack. */
    template< typename u >
    auto b( u &c ) -> decltype( begin( c ) ) { return begin( c ); }
    template< typename u >
    auto e( u &c ) -> decltype( end( c ) ) { return end( c ); }
}

template< typename iter >
struct range_proxy {
    range_proxy( iter &in_first, iter in_last )
        : first( in_first ), last( in_last ) {}

    template< typename T >
    range_proxy( iter &out_first, T &in_container )
        : first( out_first ),
        last( unpolluted::e( in_container ) ) {
        out_first = unpolluted::b( in_container );
    }

    range_iterator_reference_wrapper< iter > begin() const
        { return first; }
    range_iterator_reference_wrapper< iter > end()
        { return last; }

    iter &first;
    iter last;
};

template< typename iter >
range_proxy< iter > visible_range( iter &in_first, iter in_last )
    { return range_proxy< iter >( in_first, in_last ); }

template< typename iter, typename container >
range_proxy< iter > visible_range( iter &first, container &in_container )
    { return range_proxy< iter >( first, in_container ); }

Kullanım:

#include <vector>
#include <iostream>
std::vector< int > values{ 1, 3, 9 };

int main() {
    // Either provide one iterator to see it through the whole container...
    std::vector< int >::iterator i;
    for ( auto &value : visible_range( i, values ) )
        std::cout << "# " << i - values.begin() << " = " << ++ value << '\n';

    // ... or two iterators to see the first incremented up to the second.
    auto j = values.begin(), end = values.end();
    for ( auto &value : visible_range( j, end ) )
        std::cout << "# " << j - values.begin() << " = " << ++ value << '\n';
}

13

Bunun üzerinde kendimi denedim ve bir çözüm buldum.

Kullanım:

for(auto i : ForIterator(some_list)) {
    // i is the iterator, which was returned by some_list.begin()
    // might be useful for whatever reason
}

Uygulama o kadar da zor değildi:

template <typename T> struct Iterator {
    T& list;
    typedef decltype(list.begin()) I;

    struct InnerIterator {
        I i;
        InnerIterator(I i) : i(i) {}
        I operator * () { return i; }
        I operator ++ () { return ++i; }
        bool operator != (const InnerIterator& o) { return i != o.i; }
    };

    Iterator(T& list) : list(list) {}
    InnerIterator begin() { return InnerIterator(list.begin()); }
    InnerIterator end() { return InnerIterator(list.end()); }
};
template <typename T> Iterator<T> ForIterator(T& list) {
    return Iterator<T>(list);
}

ah, pekala evet. Derleyicinin T'sini yapıcıdan alabileceğini tam olarak anlayamadım ... bu yüzden decltype'ı düşündüm ve kullanım bloatını gördüm ... ve T'sini bir işlevden alabileceğini görmedim ... işlev şablonu, teşekkürler. Doğru mu, şimdi nasıl yapacağım?
yük

2
Evet, bu iyi görünüyor. FWIW, orada boost::counting_iteratortam olarak bunu yapar, ve uygun olan sarıldığı olsa boost::counting_rangeyazabilirsiniz, böylece: for(auto it : boost::counting_range(r.begin(), r.end())). :)
Xeo

1
Bence operator++()bir dönmelidir InnerIterator, aksi halde çok güzel ve uesful.
Ben Voigt

2

aralık tabanlı for döngü, foreachdizi öğelerinin kolay yinelenmesine izin veren java için c ++ karşılığı olarak oluşturulur . Basitleştirmek için yineleyiciler gibi karmaşık yapıların kullanımını kaldırmak içindir. Ben bir istiyorum iteratorNavaz söylediği gibi, normal kullanmak zorunda olacak fordöngü.


Bunun yerine yineleyiciler kullanan benzer bir döngü sunsalar da keşke :(
小 太郎

1
Benim için aralık temelli forsözdizimi şekeri ve yazım miktarını azaltmakla ilgili olduğundan , aldığınız şeyin yineleyici değil, değer vermelerine sevindim . Yineleyicinin referansını kaldırmak zorunda kalmak, özellikleauto
TeaOverflow

2

Bunu yapmanın çok basit bir yolu var std::vector da işlem sırasında vektörü yeniden boyutlandırıyorsanız da işe yarayacaktır (kabul edilen cevabın bu durumu dikkate alıp almadığından emin değilim)

Eğer bvektörünse, yapabilirsin

for(auto &i:b){
    auto iter = b.begin() + (&i-&*(b.begin()));
}

nerede itergerekli yineleyiciniz olacak.

Bu, C ++ vektörlerinin her zaman bitişik olması gerçeğinden yararlanır .


2
Zaten C ++ vektörler bitişik olduğu gerçeğini istismar varsa, siz de aynı zamanda herhangi bir aklı başında uygulaması sadece typedef gerçeğini patlatabilmeler vector<T>::iteratoriçin T*kontrol edin bir o: static_assert(), o zaman sadece kullanmak T* iter = &i;.
cmaster - reinstate monica

1

Bunu çok kirli yapalım ... Biliyorum, 0x70h yığın kullanımıyla, derleyici sürümüyle değişiyor .... Derleyici tarafından açığa çıkarılmalıdır, ancak değil :-(

char* uRBP = 0; __asm { mov uRBP, rbp }
Iterator** __pBegin = (Iterator**)(uRBP+0x70);
for (auto& oEntry : *this) {
    if (oEntry == *pVal) return (*__pBegin)->iPos;
}

1
Hiçbir sözüm yok, bu pek çok düzeyde yanlış, onu eleştirmeye nereden başlayacağımı bile bilmiyorum.
swineone
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.