11

We use Integer type represent index variables most of the time. But in some situations, we're forced to choose

std::vector<int> vec;
....

for(int i = 0; i < vec.size(); ++i)
....

This will cause the compiler to raise the warning that mixed use of signed/unsigned variables. if I make the index variable as for( size_t i = 0; i < vec.size(); i++ ), (or an unsigned int )it will sort out the issues.

When it come more specific to use windows types, most of the Windows APIs are dealing with DWORD (which typedef-ed as unsigned long).

So when I use similar iteration, will again cause the same warning. Now if I rewrite it as

DWORD dwCount;
....

for(DWORD i = 0; i < dwCount; ++i)
....

I find this a bit weird. It might be the problem withe perceptions.

I agree that we are supposed to use the same type of index variable to avoid the range problems can happen with the index variables. For e.g if we're using

_int64 i64Count; // 
....

for(_int64 i = 0; i < i64Count; ++i)
....

But in the case of DWORD, or unsigned integers, are there any problems in rewriting it as

for(int i = 0; (size_t)i < vec.size(); ++i)

How most of the people are working with similar issues?

3
  • 4
    Why would you use a signed integer to represent index? That's like using vector of integers to store a string. Aug 30, 2011 at 9:46
  • 3
    @Let_Me_Be: Because it makes the boundary conditions easier to test. For example on a count down to zero, a less-than test before executing the loop body cannot work with an unsigned value. Similarly a count up to the maximum doesn't work. Of course in that case a signed integer won't work either (because it can't represent such a large value).
    – Yttrill
    Jan 13, 2012 at 20:03
  • "This will cause the compiler to raise the warning that mixed use of signed/unsigned variables." That's only one of the two problems you'll have to contend with. In many cases std::size_t is a higher rank than int (or even long). If the vector's size ever exceeds std::numeric_limits<int>::max(), you'll regret having used int. Jan 24, 2018 at 0:31

6 Answers 6

11

vector has a typedef that tells you the correct type to use :-

for(std::vector<int>::size_type i = 0; i < thing.size(); ++i)
{
}

It's almost always defined to be size_t though but you can't rely on that

6
  • 8
    Not really an improvement in readability, IMHO.
    – Doc Brown
    Aug 30, 2011 at 13:12
  • No, but as it's the only way to tell what is the correct type to use for an index into the vector, that doesn't really matter...
    – JohnB
    Aug 30, 2011 at 13:26
  • 4
    These days in c++11 just use auto
    – JohnB
    Jan 1, 2014 at 9:37
  • 7
    @JohnB You mean like auto i = 0? That doesn't help at all, i become an int.
    – Emil Laine
    Mar 27, 2015 at 3:42
  • 1
    Readability can be improved, with using index_t = std::vector<int>::size_type;. Oct 19, 2017 at 9:00
4
std::vector<int> vec;

for(int i = 0; i < vec.size(); ++i)

Use an iterator for this, not a for loop.

For the others, as long as the variable type is of the same size, static_cast should work just fine (i.e. DWORD to int16_t)

1
  • 3
    for (std::vector<int>::iterator i = vec.begin(); i != vec.end(); ++i) is a pain to write. Having for (auto i = vec.begin();... is a whole lot more readable. Of course, foreach is also in C++11. Aug 30, 2011 at 14:12
4

The case you described is one of the things I dislike in C++, too. But I have learned to live with that, either by using

for( size_t i = 0; i < vec.size(); i++ )

or

for( int i = 0; i < (int)vec.size(); i++ )

(of course, the latter only when there is no risk of getting some int overflow).

4

The reason it's warning you about comparison between signed and unsigned is because the signed value will likely be converted to unsigned, which might not be what you expect.

In your example (comparing int to size_t), int will be implicitly converted to size_t (unless int somehow has a larger range than size_t). Thus, if the int is negative, it will likely be greater than the value you're comparing it to due to wraparound. This won't be a problem if your index is never negative, but you'll still get that warning.

Instead, use an unsigned type (such as unsigned int, size_t, or, as John B recommends, std::vector<int>::size_type) for your index variable:

for(unsigned int i = 0; i < vec.size(); i++)

Be careful when counting down, though:

for(unsigned int i = vec.size()-1; i >= 0; i--) // don't do this!

The above will not work because i >= 0 is always true when i is unsigned. Instead, use the "arrow operator" for loops that count down:

for (unsigned int i = vec.size(); i-- > 0; )
    vec[i] = ...;

As other answers point out, you normally want to use an iterator to traverse a vector. Here's the C++11 syntax:

for (auto i = vec.begin(); i != vec.end(); ++i)
1
  • 1
    This still runs the risk that an unsigned int isn't large enough to hold the size. Jan 24, 2018 at 0:39
2

A new option for C++11, you can do things like the following

for(decltype(vec.size()) i = 0; i < vec.size(); ++i) {...}

and

for(decltype(dWord) i = 0; i < dWord; ++i) {...}

While it repeats a little more than the basic for-loop would, it's not nearly as long-winded as pre-'11 ways of specifying values, and using this pattern consistently will work for most, if not all, possible terms you'd want to compare against, which makes it great for code refactoring. It even works for simple cases like this:

int x = 3; int final = 32; for(decltype(final) i = x; i < final; ++i)

Furthermore, while you should use auto whenever you're setting i to some intelligent value (like vec.begin()), decltype works when you're setting to a constant like zero, where auto would just resolve that to int because 0 is a simple integer literal.

To be honest, I'd like to see a compiler mechanism for extending the auto type-determination for loop incrementers to look at the value being compared against.

1

I use a cast to int, as in for (int i = 0; i < (int)v.size(); ++i). Yes, it's ugly. I blame it on the stupid design of the standard library where they decided to use unsigned integers to represent sizes. (In order to.. what? extend the range by one bit?)

5
  • 1
    In what situation would a collection size of negative anything be meaningful? Using unsigned integers for various collection sizes seems like the sensible choice, to me. Last I checked, taking the length of a string rarely returned a negative result either...
    – user
    Aug 31, 2011 at 8:11
  • 2
    In what situation would it be meaningful for simple test such as if(v.size()-1 > 0) { ... } to return true for an empty container? The problem is that sizes are also often used in arithmetic, esp. with index-based containers, which is asking for trouble given that they're unsigned. Basically, using unsigned types for anything else than 1) bitwise manipulations, or 2) modular arithmetic is calling for trouble.
    – zvrba
    Sep 1, 2011 at 6:11
  • 2
    good point. While I don't really see the point of your particular example (I would probably just write if(v.size() > 1) { ... } since that makes the intent more clear, and as an added bonus the issue of signed/unsigned becomes void), I do see how in some specific cases signedness might be useful. I stand corrected.
    – user
    Sep 2, 2011 at 20:59
  • 1
    @Michael: I agree it was contrived example. Still, I often write algorithms with nested loops: for(i=0;i<v.size()-1;++i) for(j=i+1;j<v.size();++j) .. if v is empty, the outer loop executes (size_t)-1 times. So I either have to check v.empty() before the loop or cast v.size() to a signed type, both of which I personally think are ugly workarounds. I choose a cast as it's fewer LOC, no if()s => fewer possibilites for mistake. (Also, in 2nd complement, conversion oveflow returns a negative number, so the loop doesn't execute at all.)
    – zvrba
    Sep 3, 2011 at 9:44
  • 1
    Extending the range by 1 bit was (and continues to be) very useful in 16-bit systems. Jan 24, 2018 at 0:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.