What ist a recommended way to do arithmetic with for loop indices when using size_type in C++?

291 Views Asked by At

I had a strange error in my program and it came down to a (for me) unexpected behaviour when doing a substraction between an int and a vector::size_type. Here is a simple example:

    #include <iostream>
    #include <vector>

    typedef std::vector<double> state_type;

    int n = 1;

    int main() {

       state_type::size_type i = 0;
       std::cout << i - n << std::endl;

    }

I expected this program to prints -1, but it prints (compiled with icc 14.0.1 under 64bit Linux) :

    18446744073709551615

My current explanation is that size_type is unsigned and this leads to some weird (?) conversion ? I only found this error after a long time and I am very surprised this happens. Now my question is how can I avoid these kind of mistakes, lets say in some for loop iterating over a std::vector:

   int n = 2;
   for(state_type::size_type i = 0; i < my_vec.size(); ++i) {
        if(i - n >= 0)
            my_vec[i - n] += 3;
   }

Of course I could take i as an int, but isn't it recommended to use size_type for these kind of loops ? What is a better way to do it ? Or am I the only one who is surprised by this conversion ?

3

There are 3 best solutions below

2
Basile Starynkevitch On

Indeed in std::vector the type size_type is documented as "an unsigned integral type" [...] "usually the same as std::size_t" (on my Debian/Sid/x86-64 it is a 64 bits unsigned integer). Probably passing -Wall to g++ would have warned you.

So you better cast explicitly (perhaps to std::ptrdiff_t), e.g. use

int n = 2;
for(state_type::size_type i = 0; i < my_vec.size(); ++i) {
     if((std::ptrdiff_t) i - (std::ptrdiff_t) n >= 0)
         my_vec[i - n] += 3;
}

But in C++11 (with a recent compiler such as GCC 4.8.2) I would instead code perhaps:

   for (auto it= my_vec.begin(); it!=my_vec.end(); it++) {
     if (it > my_vec.begin()+n)
       *it += 3;
   }
4
James Kanze On

This is a design flaw in the C++ standard library. The unsigned types in C++ are a bit special, and should not be used for arithmetic quantities. The way signed and unsigned mix is even more special, and should be avoided even more. The solution is to not use size_t or the size_type typedefs, and in all but extremely limited cases, to explicitly cast the return values of container::size(), etc. to a signed integral type. (In most applications, int is safe; when in doubt, ptrdiff_t can be used to avoid overflow.)

0
wacky6 On

Standard C++ defines certain implicit conversion rules. signed integer type is converted to unsigned integer type if used together in an arithmetic expression, so when you subtraction, an arithmetic downflow occurs, resulting in (uint64_t)-1

size_type is documented as unsigned int. you can use size_t instead of int, and it's better to use iterator to iterate over a container.