Reputation: 210402
I do realize the title may sound silly at first, but please, bear with me for a moment. :)
Ever since I've started using size_t
and ptrdiff_t
, I haven't had a use for int
, as far back as I can remember.
The only integer data types I remember using recently fall into one of these categories:
(Unsigned) integers associated with an index into some in-memory data structure (e.g. vector
).
Almost always, the most appropriate type for this is size_t
(or ...::size_type
, if you're going the extra mile).
Even if the integer doesn't actually represent an index, often times it's still associated with some index, so size_t
is still appropriate.
Signed versions of size_t
. In many cases, the most suitable type for this seems to be ptrdiff_t
, because often times when you need this, you're working with iterators -- and hence size_t
and ptrdiff_t
are both appropriate for them.
long
. I occasionally need this for _InterlockedIncrement
(reference counting).
(unsigned
) long long
, used for holding file sizes.
unsigned int
or unsigned long
, useful for "counting" purposes (e.g. every 1 million iterations, update the UI).
unsigned char
for raw byte-level access to memory.
(Side note: I never found a use for signed char
either.)
intptr_t
and uintptr_t
for occasionally storing operating system handles, pointers, etc.
One particular aspect of int
that's important is that you shouldn't overflow it (since it's undefined behavior), so you can't even use it reliably for counting -- especially if your compiler defines it to be 16 bits.
So when, then, should you use int
(aside from when a dependency of yours already requires it)?
Is there any real use for it nowadays, at least in newly written, portable code?
Upvotes: 8
Views: 299
Reputation: 210402
I finally found a good use case myself:
int
is the perfect candidate for storing integer logarithms, such as the zoom level on a map.
short
usually doesn't have enough precision for this (the rotation of a smooth mouse wheel can be very precise), and long
is usually more precision than we need.
We definitely need negative values, so we shouldn't be using unsigned types. Hence int
fits the bill.
Upvotes: 0
Reputation: 106086
Luchian has some excellent readability points, to which I'll add some technical ones:
int
that's efficient to deal with, whereas long
might not be (risks more CPU cycles per operation, more bytes of machine code, more registers needed etc.)abs(a - b)
looks right mathematically but doesn't give the intuitive result when b
> a
and they're unsignedint second_delta = (x.seconds - y.seconds) + (x.minutes - y.minutes) * 60;
if (pending - completed > 1) kick_off_threads()
-1
is often used: for unsigned types this will be converted to the largest possible value, but that can lead to misunderstandings and coding errors (e.g. if (x >= 0)
test for non-sentinel)Also, there's a lot of scope for implicit conversions between signed and unsigned integers - it's important to understand that unsigned types rarely help enforce a "non-negative" invariant: if that's part of the appeal, you're better off writing a class with constructor and operators enforcing the invariant.
On the readability side, int
denotes a general need for a number that clearly spans the problem domain - it may be excessive but it's known cheap in machine code ops and CPU cycles so is the go-to type for general integral storage. If you start using say unsigned char
to store someone's age - not only does it not play well with operator<<(std::ostream&...)
, but it begs questions like "was there some need to conserve memory here?" (especially confusing for stack-based variables), "is there some intention to treat this as binary data for I/O or IPC purposes?", or even "is it a known single-digit age stored in ASCII?". If something's likely to end up in a register anyway, int
is a natural sizing.
Upvotes: 6
Reputation: 327
Let's say you were implementing a program for a bank account. If you withdraw too much (which some accounts allow you to, with a penalty), then you will need to represent a negative value some way.
Upvotes: 0
Reputation: 258568
How about the most important reason of all - readability. (and simple math)
long weeklyHours = daysWorked * hoursPerDay;
"Okay... but then again, how much can a human actually work per week that we need a long
"
size_t weeklyHours = daysWorked * hoursPerDay;
"Wait... are we using weeklyHours
to iterate over a vector?"
unsigned int weeklyHours = daysWorked * hoursPerDay;
"Clear enough." - possible source for errors if either can be negative (part of the logic - it could be a way to account for time off or leave, not important)
int weeklyHours = daysWorked * hoursPerDay;
"Okay, simple enough. I get what this is doing."
Upvotes: 9