Yes, you are right. I was thinking to myself that if I made them unsigned, I wouldn't even need the test for 'less than 0', since that would never happen in that case anyway.
Not only should they be logically unsigned, but they should also be size_t or (if you want to be properly pedantic) std::string::size_type.
The reason for this is because many string functions use std::string::npos as a constant. It's defined as (basically) size_t(-1).*
That is to say, the highest value a size_t variable can hold (since it's unsigned, and the -1 loops back around).
If you do this:
unsigned pos = myStr.find("blah"); if(pos == std::string::npos) return false;
...then ya got a bug. 'unsigned' and 'int' are both 32 bits even on 64 bit computers.* But 'size_t' is 64 bit on 64 bit computers.*
So if 'pos' is merely an 'unsigned' variable, it'd never be true.
if(my32BitInteger == TheHighestValueA64BitIntegerCanHold) return false;
*Usually, on modern personal computers, using the commonly-used compilers, but not guaranteed.
In my own code, if I'm making assumptions about variable sizes, I use uint32_t, uint64_t, and etc...
But when I'm using std::string's functions, I have to use std::string's assumptions (std::string::size_type - or in my code, I use size_t because it's most likely equivalent).
If you can use C++11 you should really be using auto. Doing so guarantees the correct type (instead of "most likely equivalent") and also prevents you from accidentally using an uninitialized variable.