Потрібен ітератор, коли використовується цикл на основі діапазону


84

На даний момент я можу робити лише цикли на основі діапазону з цим:

for (auto& value : values)

Але іноді мені потрібен ітератор значення, а не посилання (З якоїсь причини). Чи існує який-небудь метод без необхідності проходити весь вектор, порівнюючи значення?

Відповіді:


77

Використовуйте старий 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
 }

Такий підхід дозволяє уникнути Searching дається value, так як valueі itзавжди синхронізовані.


Так, я цим займався. Мені просто було цікаво, чи є рішення із заміщеними циклами на основі
小 太郎

4
Я згоден, що перше рішення зі старим циклом for набагато краще: P
小 太郎

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

1
@David: Що робити, якщо у векторі є дублікати? 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 і побачив використання-bloat ... і я не бачив, що він може отримати його T з функції ... шаблон функції, спасибі. Чи правильно, як я це роблю зараз?
корисне навантаження

2
Так, це виглядає добре. FWIW, є boost::counting_iteratorправда, яка робить саме те, що і зручно обмотують boost::counting_range, так що ви можете написати: for(auto it : boost::counting_range(r.begin(), r.end())). :)
Xeo

1
Думаю, operator++()слід повернути InnerIterator, інакше дуже приємне та корисне.
Ben Voigt

2

forЦикл на основі діапазону створюється як аналог c ++ для foreachin Java, що дозволяє легко ітерацію елементів масиву. Він призначений для усунення використання складних структур, таких як ітератори, щоб зробити це простішим. Якщо ви хочете iterator, як сказав Наваз, вам доведеться використовувати звичайний forцикл.


Хотілося б, щоб вони запропонували подібний цикл, який замість цього використовував ітератори :(
小 太郎

1
Я щасливий, що те, що ти отримуєш, це їх цінність, а не ітератор, адже для мене діапазон for- це синтаксис цукру та зменшення кількості набору тексту. auto
Потрібно розіменовувати

2

Існує дуже простий спосіб зробити це std::vector, який також повинен працювати, якщо ви змінюєте розмір вектора під час процесу (я не впевнений, чи прийнята відповідь розглядає цей випадок)

Якщо bце ваш вектор, ви можете просто зробити

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

де iterбуде ваш необхідний ітератор.

Це використовує той факт, що вектори С ++ завжди суміжні .


2
Якщо ви вже використовуєте той факт , що вектори С ++ є суміжними, ви можете також використовувати також той факт , що будь-яка реалізація розсудливою просто ЬурейеЕ 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
У мене немає слів, це неправильно на багатьох рівнях, я навіть не знав би, з чого почати це критикувати.
свинар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.