Необходим итератор при использовании циклов for на основе диапазона


85

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

for (auto& value : values)

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

Ответы:


78

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

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

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


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

Хотя я бы не рекомендовал это, но если вы хотите использовать forцикл на основе диапазона (да, по какой-то причине : 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всегда синхронизированы.


Да, это то, что я делал. Мне просто интересно, есть ли решение с циклами на основе ранжирования вместо этого
小 太郎

4
Я согласен, что первое решение со старым циклом for намного лучше: P
小 小

@ 小 太郎: Или вы можете использовать, std::findесли вам нужно найти значение ... Старые добрые алгоритмы все еще находятся в новом стандарте.
Дэвид Родригес - dribeas 05

1
@ Дэвид: Что делать, если в векторе есть дубликаты? valueи itможет не синхронизироваться. Помните value, это ссылка.
Nawaz

9
@Nawaz: Думаю, я неправильно понял последнее предложение. Я думал, что он использовал диапазон, основанный на поиске известного объекта. Кстати, предпочитают , ++itчтобы по it++мере возможности (оба использования в вашем коде) , как это могло бы иметь меньшую нагрузку.
Дэвид Родригес - dribeas

15

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

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

13

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

Применение:

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

ах, ну да. Я не совсем понял, что компилятор может получить свой T из конструктора ... поэтому я подумал о decltype и увидел раздувание использования ... и я не видел, что он может получить свой T из функции ... шаблон функции, спасибо. Правильно, как я сейчас это делаю?
полезная нагрузка

2
Да, выглядит хорошо. FWIW, есть boost::counting_iteratorправда, которая делает именно то, что и удобно обматывают boost::counting_range, так что вы можете написать: for(auto it : boost::counting_range(r.begin(), r.end())). :)
Xeo

1
Я думаю operator++()должен вернуть InnerIterator, в противном случае очень мило и уродливо.
Бен Фойгт

2

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


Я бы хотел, чтобы они предложили аналогичный цикл, в котором вместо этого использовались бы итераторы :(
小 太郎

1
Я счастлив, что вы получаете то, что они ценят, а не итератор, потому что для меня диапазон основан forна синтаксическом сахаре и о сокращении количества наборов. Необходимость разыменовать итератор сделает его подверженным ошибкам, особенно при использовании сauto
TeaOverflow

2

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

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

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

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

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


2
Если вы уже используете тот факт, что векторы C ++ являются смежными, вы также можете использовать тот факт, что любая разумная реализация будет просто typedef vector<T>::iteratorдля T*: Проверьте это с помощью static_assert(), а затем просто используйте T* iter = &i;.
cmaster - восстановить

1

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

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

1
У меня нет слов, это неправильно на многих уровнях, я даже не знаю, с чего начать критиковать.
swineone
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.