0

Is the following code defined behavior, I mean i-- when i is 0 will always be the biggest unsigned int representable by size_t, right ? so is this totally safe to use it in my code ?

size_t NPOS = -1;
for(size_t i = vec.size()-1; i!= NPOS; i--)
user152508
  • 3,053
  • 8
  • 38
  • 58
  • Provided your platform uses 2's complement to represent signed integers, yes. – heinrichj Apr 23 '14 at 06:48
  • 1
    @heinrichj, It still has to work in 1's complement as well. See C++11 § 4.7 [conv.integral]/2. [This question](http://stackoverflow.com/questions/22131388/is-conversion-int-unsigned-long-long-defined-by-the-standard) might also be of some interest. – chris Apr 23 '14 at 06:51
  • 1
    Use `i-->0` instead: http://stackoverflow.com/questions/1642028/what-is-the-name-of-this-operator – dalle Apr 23 '14 at 06:56
  • 1
    You know that vector has reverse iterators to do that? – Jens Apr 23 '14 at 07:24
  • @dalle Why? To obfuscate, or ? – James Kanze Apr 23 '14 at 08:18
  • @JamesKanze: On the contrary, to unobfuscate. If find it easier to read `for(size_t i = vec.size(); i-- > 0;)` or `size_t i = vec.size(); while(i-- > 0)` than using the `NPOS` approach. – dalle Apr 23 '14 at 10:02
  • @dalle What `NPOS` approach? If a condition has side effects, it's obfuscation. A reader who sees a `while` doesn't look for side effects. A reader doesn't expect side effects in the second part of a `for` either. (The more or less standard way for writing this sort of loop, assuming no reverse iterators, would be `while ( i > 0 ) { --i; /* ... */ }`. At least in the code I've seen.) – James Kanze Apr 23 '14 at 13:10

4 Answers4

4

This is a classic scenario for using the fabulous goes-to operator:

for (size_t i = vec.size(); i --> 0; )
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
1

In class std::basic_string static data member npos is defined the following way

static const size_type npos = -1;

Take into account that the type of the return value of member function size() is size_type. So your code is valid

size_t NPOS = -1;
for(size_t i = vec.size()-1; i!= NPOS; i--)

provided that the value of expression vec.size()-1can be stored in type size_t

so the more valid code will look as

for ( std::vector<T>::size_type i = vec.size()-1; i != std::vector<T>::npos; i-- )

Though I would write the loop the following way

for ( std::vector<T>::size_type i = vec.size(); i != 0;  )
{
   //...
   // using expression vec[--i]
   // or
   //  --i
   // vec[i]
   //...
} 
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

Technically, it works, since the converion of -1 to an unsigned type is guaranteed to result in the largest possible value, as is decrementing from 0. Still, it's not what I would consider good practice. If you just want to iterate over one vector in reverse:

for ( auto current = vec.rbegin(); current != vec.rend(); ++ current ) {
    //      Do something with *current
}

And if for some reason you do need the index:

int i = vec.size();
while ( i != 0 ) {
    -- i;
    //      Do something with vec[i]
}

This loop is much cleaner, and avoids any issues with unsigned types wrapping (although if you need the index, that probably means you need it as an arithmetic value, so you should avoid unsigned types to begin with).

James Kanze
  • 150,581
  • 18
  • 184
  • 329
0

All of these will be the same value for a given integer type T, signed or unsigned:

T a = -1;
T b = 0 - 1;
T c = 0; c = c - 1;
T d = 0; d --;
T e = 0; -- e;

assert(a == b);
assert(a == c);
assert(a == d);
assert(a == e);

That covers all of the usages you have in your snippet. The internal representation of the integer doesn't really matter. The fact that a signed -1 converts to the maximum value of a similar unsigned integer is interesting but not directly significant. The above is true else arithmetic stops working.

Jason C
  • 38,729
  • 14
  • 126
  • 182