01 Feb 12
16:08

std::vector size() method and gotcha with unsigned vs signed

Guess who wins in signed vs unsigned integer conversion? I guessed wrong today, or rather, I’ve been assuming wrong for quite a while.

consider this example:

   std::vector<SomeClass*> myVec;
   int countdown = -1;
   // ... (fill the vector)
   if (countdown < myVec.size()) 
      countdown++;  // this will never get hit

myVec.size() returns size_t, which is an unsigned int and countdown is an int, so to compare these two, the compiler will implicitly cast. You will get warnings when you do this kind of comparison, so listen to them. I assumed in the conversion of primitive values, signed int has precedence. That line casts countdown to an unsigned int, meaning that it has a very high value (0xfffffff), so the conditional is always false.

So the if statment was evaluating to

   if ((unsigned int) -1 < 1) // pretend there is 1 element in the array 
      countdown++;

which is

   if (0xFFFFFFFF < 1) // false (same as  UINT_MAX < 1)
      countdown++; 

The correct code then is:

   if (countdown < (int) myVec.size()) 
      countdown++;  // this will now be run

Here are the rules for implicit type conversion from the C99 standard:

6.3.1.8 Usual arithmetic conversions

If both operands have the same type, then no further conversion is needed.
Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.
Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.
Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.

I don't understand the statement after the bold section. But it is clear that unsigned types have precedence over same ranked types.