Question

I was using -1 as a flag value for a function whose return type is size_t (an unsigned type).

I didn't notice it at first, particularly because it wasn't causing any errors in my code (I was checking it with x == -1, not x < 0).

Are there any subtle reasons I shouldn't leave it as is? When might this behave unexpectedly? Is this commonly used?

ptrdiff_t is less common, takes longer to type, and anyway it's not really the appropriate type since the function returns an index into an array.

Was it helpful?

Solution 3

After trying to think of ways this might go wrong, I realized that there's a danger that the calling function might implicitly cast the return value to a larger type (ie unsigned int to unsigned long long). Then checking if that value == -1 will be false.

The safer option is to explicitly use size_t.max as the sentinel value. I'm always uncomfortable with changing between signed and unsigned types. Sometimes I think the more reasonable approach is to just make everything signed (like Java does).

OTHER TIPS

-1 will always convert to the max unsigned value, this is due to section 4.7 Integral conversions:

If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). —end note ]

The same quote for C99 would be from 6.3.1.3:

Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.49)

So we end up with:

-1 + (UMAX + 1)

which is:

UMAX

The obvious caveat lies in the case of a set of elements with a size equal to the largest size possible. The possibility and usability of this happening in practice and actually being the cause of your problem at that point are negligible.

If you look at the C++ std::string class, you will notice the static std::string::npos data member is defined as exactly -1 converted to std::string::size_type (which is really just std::size_t. That gives this "technique" a sense of precedence, which allows it to fullfil The Principle of Least Surprise™, which is always a Good Thing®.

Now, using -1 directly in a comparison like that is asking for trouble. You should, as in the std::string case, ensure there is an accessible name for this value that will ensure its special meaning. unfortunately, the C++ type system isn't strict enough for this to prevent a user from shooting himself in the foot, but at least a user adhering to documented best practice won't think of doing things differently.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top