Как выполнить итерацию по вектору, а также узнать индекс элемента?

Мне нужно получить доступ к каждому элементу в векторе, а также знать, в каком индексе находится элемент.

До сих пор я мог придумать два способа

 for (iterator it= aVector.begin(), int index= 0; it!= aVector.end(); ++it, ++index)

оставив подпись типа. также похоже, что я не могу использовать авто

 for (int index = 0; index < aVector.size(); ++index)
{
    // access using []
}

Какой из них более эффективен или есть лучший способ сделать это?


person unj2    schedule 19.09.2012    source источник
comment
Если вам нужен индекс по какой-то внутренней, фундаментальной причине, используйте вторую версию.   -  person Kerrek SB    schedule 19.09.2012
comment
Да, предпочитайте последнее, если вам нужен index.   -  person Tony The Lion    schedule 19.09.2012
comment
Какой из них более эффективен? Почему бы просто не запустить цикл и не измерить время? Что касается личных предпочтений, я бы выбрал второй ... всегда. Вы только посмотрите, какой уродливый первый.   -  person Shahbaz    schedule 19.09.2012
comment
Вы также можете использовать std::distance, если вы не можете контролировать, куда идет итератор from и не хотите сами писать цикл. Но на самом деле это просто цикл от первого ко второму и подсчет, ничего особенного. (EDIT: на самом деле это не в случае vector, это обман и использование того факта, что на самом деле это просто массив внизу).   -  person BoBTFish    schedule 19.09.2012
comment
@BoBTFish: это не мошенничество и не использование того факта, что это массив; он использует тот факт, что итератор имеет произвольный доступ, как говорит стандарт.   -  person Mike Seymour    schedule 19.09.2012
comment
Первый не скомпилируется, поэтому я говорю второй.   -  person Rontogiannis Aristofanis    schedule 19.09.2012


Ответы (8)


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

for (auto it = aVector.begin(); it != aVector.end(); ++it) {
    int index = std::distance(aVector.begin(), it);
}

Для контейнеров без произвольного доступа [] недоступен, а std::distance неэффективен; в этом случае, если вам нужен индекс, первый метод будет лучше (хотя вам нужно будет исправить его, чтобы он не пытался объявлять две переменные разных типов в for-initializer).

person Mike Seymour    schedule 19.09.2012
comment
Вот это я и хотел сказать :p - person matiu; 19.09.2012
comment
Это должно быть исправлено, чтобы отразить тот факт, что std::distance возвращает std::iterator_traits<InputIt>::difference_type, который будет более широким типом, чем int. (Тот факт, что мало кто будет иметь контейнеры, достаточно большие, чтобы переполнить int, не является оправданием.) Еще одна причина просто использовать auto - странно, что вы сделали это для it, но не для index. - person underscore_d; 19.07.2017

Ответ содержится в вопросе - "узнать, в каком индексе находится элемент.".

So -

for (int index = 0; index < aVector.size(); ++index)
{
    // access using []
}

С точки зрения производительности они одинаковы (но вы всегда можете профилировать себя).

person Luchian Grigore    schedule 19.09.2012
comment
индекс должен быть size_t вместо int - person Rémi; 10.04.2017
comment
@Rémi педантично, это должен быть vector‹T›::size_type :) - person Luchian Grigore; 11.04.2017
comment
@Rémi, есть ли причина, по которой можно использовать size_t вместо int? - person CrazyVideoGamer; 06.04.2021
comment
Да. В большинстве современных компиляторов int составляет 32 бита, а size_t — 64 бита. Поэтому использование int не будет работать, если у вас очень большой вектор. - person Rémi; 06.04.2021

Другой путь.

int count = 0;
for (auto& it : aVector) {
   count++;
}
person YScharf    schedule 15.03.2021
comment
Сначала я подумал про себя, наверняка, при восьми ответах, кто-то уже упоминал об этом; в конце концов, это самый простой и понятный подход. Неа. +1 вам. - person Sneftel; 15.03.2021

Вы можете использовать адаптер Boost.Range indexed, который расширяет итераторы диапазона с помощью метода index, который возвращает текущий индекс (да).

#include <boost/range/adaptor/indexed.hpp>

// ...
auto&& r = vec | boost::adaptors::indexed(0);
for(auto it(begin(r)), ite(end(r)); it != ite; ++it)
  std::cout << it.index() << ": " << *it << "\n";

К сожалению, поскольку index является частью метода итератора, это означает, что вы не можете использовать новый цикл for на основе диапазона или даже BOOST_FOREACH, которые предоставляют доступ только к элементам. Вот довольно шаблонный обходной путь сомнительной ценности:

// note: likely contains typos or bugs
#include <boost/range/adaptors.hpp>

template<class IndexIt>
auto pair_index_value(IndexIt it)
    -> std::pair<std::size_t, decltype(*it)>
{
  return std::pair<std::size_t, decltype(*it)>(it.index(), *it);
}

// ...
using namespace boost::adaptors;

auto&& ir = vec | indexed; // because screw you Boost.Range
for(auto&& elem : boost::counting_range(ir.begin(), ir.end()) | transformed(pair_index_value))
  std::cout << elem.first << ": " << elem.second << "\n";
person Xeo    schedule 19.09.2012

Вот решение с использованием zip_iterator и counting_iterator из библиотеки Boost.Iterator. Это, вероятно, намного излишне для вашего варианта использования, но у него есть преимущества работы с любым диапазоном (не только векторами) и он хорошо сочетается с основанным на итераторах дизайном стандартных алгоритмов, поэтому я публикую это здесь:

#include <boost/iterator/counting_iterator.hpp>
#include <boost/iterator/zip_iterator.hpp>

#include <algorithm>
#include <iostream>
#include <list>

int main()
{
    typedef std::list<int> container;

    typedef boost::tuple<
        container::iterator,
        boost::counting_iterator<container::size_type>
    > tuple_type;

    typedef boost::zip_iterator<tuple_type> it_type;

    container l{1, 2, 3, 4};

    it_type begin(tuple_type(l.begin(), 0));
    it_type const end(tuple_type(l.end(), l.size()));

    // sample use with for loop
    for (it_type it = begin; it != end ; ++it)
    {
        int value = it->get<0>();
        int index = it->get<1>();
        // do whatever you want with value and index
    }

    // sample use with standard algorithm
    auto res = std::find_if(begin, end,
        [](boost::tuple<int, int> const & t)
        { return t.get<0>() > 2; }); // find first element greater than 2

    std::cout << "Value: " << res->get<0>() << '\n' <<
                 "Index: " << res->get<1>() << '\n';
}
person Luc Touraille    schedule 19.09.2012

c++11:

for (auto i=aVector.begin(); i!=aVector.end(); ++i) {
    cout << "I am at position: " << i-aVector.begin() << endl;
    cout << "contents here is: " << *i << endl;
}

С++ старой школы:

for (vector<int>::const_iterator i=aVector.begin(); i!=aVector.end(); ++i) {
    cout << "I am at position: " << i-aVector.begin() << endl;
    cout << "contents here is: " << *i << endl;
}
person matiu    schedule 19.09.2012
comment
В первом i — это элемент вектора, а не индекс или итератор. Вы не можете получить индекс из цикла for в стиле диапазона. (В случае вектора вы можете взломать его с помощью &i-&vector[0], но это будет бесшумно и ужасно ломаться, если изменится тип контейнера, поэтому не делайте этого). - person Mike Seymour; 19.09.2012
comment
@MikeSeymour Я думаю, что ваш хак сработал бы только (?), Если бы цикл for использовал for (auto& i : aVector), иначе, как сейчас написано, i является копией элемента в векторе. - person Brian Neal; 19.09.2012
comment
@BrianNeal: Так оно и есть. Это еще одна причина не делать этого. - person Mike Seymour; 19.09.2012
comment
обновился до рабочего. Жаль, что автоматическая штука в этом случае не сработает; это было бы круто. - person matiu; 19.09.2012
comment
@matiu Не оставляйте там сломанный, просто удалите его и покажите лучшую версию. - person Brian Neal; 19.09.2012

for (iterator it = aVector.begin(), int index= 0; it!= aVector.end(); ++it, ++index)

Это не будет компилироваться. Но это не имеет особого значения, потому что пока мы говорим о std::vector, то доступ по индексу — это простая арифметика указателя и разыменование — так что в реальности так же быстро, как с итератором. Так что ваша версия 2 в порядке.

Однако я бы дополнительно оптимизировал (если вас действительно беспокоит скорость):

for (int index = 0, size = aVector.size(); index < size; ++index)
{
    // access using []
}
person Fiktik    schedule 19.09.2012
comment
Почему первый не компилируется? вы указали правильный тип итератора? - person unj2; 19.09.2012
comment
@kunj2aan: потому что вы не можете объявлять переменные несвязанных типов (iterator и int) в одном объявлении. - person Mike Seymour; 19.09.2012
comment
@kunj2aan Почему бы вам просто не попробовать. Обратите внимание, что решение простое. - person Fiktik; 19.09.2012

Чтобы быть немного подвесным, первый оператор OP не компилируется из-за того, как работает оператор запятой. Я уверен, что ОП просто использовал сокращение для iterator вместо полного имени типа, но проблема не в этом:

for (iterator it= aVector.begin(), int index= 0; it!= aVector.end(); ++it, ++index)

Оператор запятой либо разделяет два выражения (и возвращает результат второго выражения), либо используется для разделения переменных в объявлении. Первый аргумент аргумента for принимает любую форму, поэтому он скрывает тот факт, что они имеют разный синтаксис.

#include <vector>
#include <iostream>

int main()
{
    std::vector<int> aVector = {1,1,2,3,5,8,13};

    // option 1. Both loop variables declared outside the for statement, initialized inside the for statement
    int index1 = 0;
    decltype(aVector.begin()) it1;
    for (it1 = aVector.begin(), index1=0; it1!= aVector.end(); ++it1, ++index1)
    {
        std::cout << "[" << index1 << "]=" << *it1 << std::endl;
    }

    // option 2. The index variable declared and initialized outside, the iterator declared and initialized inside
    int index2=0;
    for (auto it2 = aVector.begin(); it2!= aVector.end(); ++it2, ++index2)
    {
        std::cout << "[" << index2 << "]=" << *it2 << std::endl;
    }

#if 0
    // option3 (the OP's version) won't compile. The comma operator doesn't allow two declarations.
    for (auto it3 = aVector.begin(), int index3=0 ; it3!= aVector.end(); ++it3, ++index3)
    {
        std::cout << "[" << index3 << "]=" << *it3 << std::endl;
    }
#endif
}
person Mark Lakata    schedule 17.05.2021
comment
Ну, Fiktik заметил это еще в 2012 году. - person greybeard; 18.05.2021
comment
Я показывал два варианта, чтобы приблизиться к предполагаемому синтаксису OP его примера, и расширял ответ Fiktik. - person Mark Lakata; 18.05.2021