user541686
user541686

Reputation: 210402

When do you need 'int' in C++?

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:

  1. (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.

  2. 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.

  3. long. I occasionally need this for _InterlockedIncrement (reference counting).

  4. (unsigned) long long, used for holding file sizes.

  5. unsigned int or unsigned long, useful for "counting" purposes (e.g. every 1 million iterations, update the UI).

  6. unsigned char for raw byte-level access to memory.
    (Side note: I never found a use for signed char either.)

  7. 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

Answers (4)

user541686
user541686

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

Tony Delroy
Tony Delroy

Reputation: 106086

Luchian has some excellent readability points, to which I'll add some technical ones:

  • the compiler's expected to pick a size for 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.)
  • using signed types can eliminate some errors, such as:
    • abs(a - b) looks right mathematically but doesn't give the intuitive result when b > a and they're unsigned
    • int second_delta = (x.seconds - y.seconds) + (x.minutes - y.minutes) * 60;
    • if (pending - completed > 1) kick_off_threads()
  • when needing an integral sentinel value -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

KSchmidt
KSchmidt

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

Luchian Grigore
Luchian Grigore

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

Related Questions