The Case Against int, Part 2: Why Signed Integers?
Last week, I argued that the
int type was often dangerous because its capacity depends on the implementation. In addition to its implementation-defined precision,
int has another drawback, namely that it is signed. As it turns out, unsigned integer arithmetic is often more useful than signed.
For example, a common use of integers is to count things: the number of students in a course, cars in a parking lot, records in a file, and so on. In general, counters cannot be negative; so unsigned types are often more appropriate than signed integer types such as
int. Moreover, one common use of such integers is as array or container indices or sizes. For built-in data structures, C++ provides the unsigned type
size_t and the corresponding signed type
ptrdiff_t; moreover, each library container type has its own
size_type that has room for the number of elements in the largest container of each type. So for counting things that can fit in memory, you are probably better off using
size_t or container
::size_type than you are in using
int, or even
unsigned. For counting items in files,
unsigned long is probably right.
Integers are also commonly used to deal with external quantities. These can be imported from elsewhere, such as numbers that appear in data files, or they can represent things in the physical world that naturally come in integer form, such as dates. In such cases, it is often possible to put bounds on the values that these quantities can have.
If you know these bounds, you know whether a signed or unsigned type is appropriate to deal with them. What you don't know is which specific. One reader noted that C++ has types such as
int32_t, which let you specify a specific number of bits in an integer. However, this specification isn't as useful as it looks at first.
For example, suppose we want to be able to deal with signed integers up to 1000000. If we happen to be running on a machine with 24-bit integers, we can fit such an integer in an
int. In fact, we can do so even if
int is 21 bits, including sign; but there's no
int21_t type available for the purpose.
What is available is code like this:
typedef decltype(1000000) million_int;
This code takes advantage of the
decltype feature of C++11 to determine the type of the literal
1000000. That type, in turn, is defined to be the first of
long long that has enough room to contain this particular value on this implementation. It is then possible to use
million_int wherever a signed integer type is needed that has room for
We can now list our recommendations:
- If you are counting array elements, use
size_t, or its corresponding signed type
- If you are counting container elements, use container::
size_typeor the corresponding signed type container::
- If you are counting data in a file, use
- If you want to work with integers within a known range, use
decltypeon a literal to determine the corresponding type.
So far, I haven't come up with a similarly succinct recommendation for when to use
Next week, we'll look at some of the hazards of combining signed and unsigned numbers.