Нужен итератор при использовании циклических для циклов - PullRequest
79 голосов
/ 05 августа 2011

В настоящее время я могу делать только циклы на основе диапазона с этим:

for (auto& value : values)

Но иногда мне нужен итератор для значения, а не ссылка (по какой-либо причине). Есть ли способ без необходимости проходить через весь вектор, сравнивая значения?

Ответы [ 6 ]

70 голосов
/ 05 августа 2011

Используйте старый цикл for как:

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

С этим вы получите value, а также итератор it.Используйте все, что вы хотите.


РЕДАКТИРОВАТЬ:

Хотя я бы не рекомендовал это, но если вы хотите использовать основанный на диапазоне * цикл 1012 * (да, По какой-либо причине : D), тогда вы можете сделать это:

 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
 }

Этот подход позволяет избежать поиска по заданным value, поскольку value и it всегда синхронизированы.

14 голосов
/ 05 августа 2011

Вот прокси-класс-оболочка, позволяющий вам показывать скрытый итератор, присваивая ему псевдоним своей переменной.

#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 ); }

Использование:

#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';
}
10 голосов
/ 17 февраля 2013

Я попробовал себя в этом и нашел решение.

Использование:

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

Реализация не была такой сложной:

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);
}
4 голосов
/ 05 августа 2011
Цикл

на основе диапазона for создается как аналог c ++ для foreach в Java, что позволяет легко перебирать элементы массива. Он предназначен для устранения использования сложных структур, таких как итераторы, чтобы упростить его. Если вам нужен iterator, как сказал Наваз, вам придется использовать обычный цикл for.

1 голос
/ 09 февраля 2018

Давайте сделаем это очень грязно ... Я знаю, 0x70h меняется в зависимости от использования стека, версии компилятора .... Это должно быть открыто компилятором, но это не так:*

1 голос
/ 15 апреля 2017

Существует очень простой способ сделать это для std::vector, который также должен работать, если вы изменяете вектор во время процесса (я не уверен, учитывает ли принятый ответ этот случай)

Если b ваш вектор, вы можете просто сделать

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

где iter будет вашим требуемым итератором.

Это использует тот факт, что векторы C ++ всегда непрерывны .

...