简体   繁体   中英

How do I iterate over a vector and also know the index of the element?

I need to access each element in a vector and also know what index the element is in.

So far I could come up with two ways

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

leaving the type signature. also it looks like i can't use auto

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

Which one is more efficient or is there a better way to do this?

For a vector or other random-access container, it makes little difference. I would probably choose the second because it's easier to read, and is probably marginally faster since there's only one loop variable to update. Another alternative is:

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

For non-random-access containers, [] isn't available, and std::distance is inefficient; in that case, if you need the index, the first method would be better (although you'll need to fix it so it doesn't try to declare two differently-typed variables in the for-initialiser).

The answer is in the question - "know what index the element is in." .

So -

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

Performance-wise they're the same (but you can always profile yourself).

Another way.

int count = 0;
for (auto& it : aVector) {
   count++;
}

Here is a solution using zip_iterator and counting_iterator from the Boost.Iterator library . It is probably way overkill for your use case, but it has the advantages of working with any range (not only vectors) and to fit it nicely with the iterator-based design of standard algorithms, so I post it here:

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

You can use Boost.Range's indexed adaptor, which extends the range's iterators with an index method that returns the current index (duh).

#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";

Sadly, since index is part a method on the iterator, this means you can't use the new range-based for loop or even BOOST_FOREACH , which only give element access. Here's a rather boilerplate-y workaround of questionable value:

// 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";

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

c++ old school:

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;
}
for (iterator it = aVector.begin(), int index= 0; it!= aVector.end(); ++it, ++index)

This will not compile. But it doesn't really matter, because as long as we are talking about std::vector then accessing by index is a simple pointer arithmetic and dereference - so in reality as fast as with the iterator. So your version 2 is OK.

I would however further optimize (if you are really concerned about speed):

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

To be a little pendantic, the OP's first statement doesn't compile because of the way the comma operator works. I'm sure the OP was just using a shorthand for iterator instead of the full typename, but that isn't the problem:

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

The comma operator either separates two expressions (and returns the result of the second expression), or it is used to separate variables in a declaration. The first argument of the for argument will take either form, so it is obfuscates the fact that they are different syntaxes.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM