43

I need to iterate over a vector from the end to the beginning. The "correct" way is

for(std::vector<SomeT>::reverse_iterator rit = v.rbegin(); rit != v.rend(); ++rit)
{
    //do Something
}

When //do Something involves knowing the actual index, then some calculations need to be done with rit to obtain it, like index = v.size() - 1 - (rit - v.rbegin)

If the index is needed anyway, then I strongly believe it is better to iterate using that index

for(int i = v.size() - 1; i >= 0; --i)
{
    //do something with v[i] and i; 
}

This gives a warning that i is signed and v.size() is unsigned. Changing to

for(unsigned i = v.size() - 1; i >= 0; --i) is just functionally wrong, because this is essentially an endless loop :)

What is an aesthetically good way to do what I want to do which

  • is warning-free
  • doesn't involve casts
  • is not overly verbose
cigien
  • 57,834
  • 11
  • 73
  • 112
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434

10 Answers10

64

As you've noted, the problem with a condition of i >= 0 when it's unsigned is that the condition is always true. Instead of subtracting 1 when you initialize i and then again after each iteration, subtract 1 after checking the loop condition:

for (unsigned i = v.size(); i-- > 0; )

I like this style for several reasons:

  • Although i will wrap around to UINT_MAX at the end of the loop, it doesn't rely on that behavior — it would work the same if the types were signed. Relying on unsigned wraparound feels like a bit of a hack to me.
  • It calls size() exactly once.
  • It doesn't use >=. Whenever I see that operator in a for loop, I have to re-read it to make sure there isn't an off-by-one error.
  • If you change the spacing in the conditional, you can make it use the "goes to" operator.
Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • 3
    shoulnd't it be size_t than unsigned ? – dynamic Jun 28 '13 at 08:45
  • 4
    That could be better if you really anticipate having more items than will fit in an `unsigned`, @Yes123, but the formally correct type would be `std::vector::size_type`, or, nowadays, simply `auto`. – Rob Kennedy Jun 28 '13 at 13:18
17

There's nothing to stop your reverse_iterator loop also using the index as described in multiple other answers. That way you can use the iterator or index as needed in the // do the work part, for minimal extra cost.

size_t index = v.size() - 1;
for(std::vector<SomeT>::reverse_iterator rit = v.rbegin(); 
    rit != v.rend(); ++rit, --index)
{
  // do the work
}

Though I'm curious to know what you need the index for. Accessing v[index] is the same as accessing *rit.

Steve Townsend
  • 53,498
  • 9
  • 91
  • 140
  • This is actually the only correct answer for any underlying type that's not random access. Assuming `std::vector` is typedef'd away somewhere I'd prefer to use the correct iterator in case I later decided to use a list, multiset, map, etc. – Ben Jackson Nov 17 '10 at 17:06
  • 1
    use auto to reduce verbosity. – Kabira K Jul 17 '18 at 03:36
7

to be aesthetically pleasing! ;)

for(unsigned i = v.size() - 1; v.size() > i; --i)
Nim
  • 33,299
  • 2
  • 62
  • 101
  • @Armen, hmm, but unsigned (by default int) *could be* smaller than the `size_type` of `vector`, as to why you could have more than 4bn entries in your vector, that's a different question... :).. okay, okay... I give in... – Nim Nov 17 '10 at 15:36
  • And what if v.size() == UINT_MAX ? I know this never happens in practice, but it is a theoric flaw – Jean-Bernard Jansen Nov 17 '10 at 15:43
  • @JB: vector::size_type is not unsigned int is also a theoretic flaw. But I honestly don't care about theoretic flaws as long as there is really no practical chance it's gonna backfire – Armen Tsirunyan 7 secs ago edit – Armen Tsirunyan Nov 17 '10 at 15:44
  • I don't know if it's me not using the wrapping around efficiently, but I usually do it with `for (i = v.size(); i > 0; --i)` and then use `i-1` as the actual index. Matter of preference I guess. – Matthieu M. Nov 17 '10 at 15:49
3

In C++20 one can use ranges (#include <ranges>)

//DATA
std::vector<int> vecOfInts = { 2,4,6,8 };

//REVERSE VECTOR 
for (int i : vecOfInts | std::views::reverse)
{
     std::cout << i << "  ";
} 

or if it is required to save in a different variable.

//SAVE IN ANOTHER VARIABLE
auto reverseVecOfInts = std::views::reverse(vecOfInts);

//ITERATION
for (int i : reverseVecOfInts)
{
    std::cout << i << "  ";
}
Pavan Chandaka
  • 11,671
  • 5
  • 26
  • 34
  • though this approach looks nice, it has performance implications - `reverse` will do _exactly (last - first) / 2 swaps_ , which in certain cases may be a notable overhead – AntonK Apr 28 '23 at 14:13
2

I would prefer the reverse iterator variant, because it's still easy to interpret and allows to avoid index-related errors.

Sometimes you can simply use the BOOST_REVERSE_FOREACH, which would make your code look the following way:

reverse_foreach (int value, vector) {
   do_something_with_the_value;
}

Actually speaking, you can always use foreach statements for these kinds of loops, but then they become a bit unobvious:

size_t i = 0;

foreach (int value, vector) {
   do_something;
   ++i;
}
Yippie-Ki-Yay
  • 22,026
  • 26
  • 90
  • 148
0

Try out a do while :

std::vector<Type> v;
// Some code 
if(v.size() > 0)
{
    unsigned int i = v.size() - 1;
    do
    {
        // Your stuff
    }
    while(i-- > 0);
}
Jean-Bernard Jansen
  • 7,544
  • 2
  • 20
  • 17
0

Hi i think better way use iterator as you use in first sample and if you need get iterator index you can use std::distance to calculate it, if i understand your question

Sanja Melnichuk
  • 3,465
  • 3
  • 25
  • 46
0

loop condition i != std::numeric_limits<unsigned>::max() ... or use UINT_MAX if you think its to verbose. or another way:
for(unsigned j=0, end=v.size(), i=end-1; j<end; --i, ++j)
or
for(unsigned end=v.size(), i=end-1; (end-i)<end; --i)

smerlin
  • 6,446
  • 3
  • 35
  • 58
-3

I think that:

for(unsigned i = v.size() - 1; i >= 0; --i)

is fine if you check

!v.empty()

earlier.

Kylo
  • 2,300
  • 1
  • 19
  • 24
  • @HardCoder1986: Think again :) – Armen Tsirunyan Nov 17 '10 at 15:15
  • the code won't work properly because of underflow error. It's better to get rid of subtracting `1` initially, and instead compare for _greater_ this way `for(unsigned i = v.size(); i > 0; --i) { ... }`, but you'll have then use `v[i-1]` to access current element by an index within the loop. – AntonK Apr 28 '23 at 14:08
-3
for (it = v.end()-1; it != v.begin()-1; --it)
{
}

The "goes to" operator definitely messes with my head.

  • 1
    @Floella AFAIK, this doesn't work (subtracting integers is not supported for iterators). To iterate in reverse, I think you have to use `std::vector::reverse_iterator` *a la* https://stackoverflow.com/a/4206142/5835083, or use `std::distance` if you really insist on using subtraction. –  Feb 25 '18 at 09:47