No, banning the builtin integer types would be absurd. They should not be abused either, however.
If you need an integer that is exactly N bits wide, use
std::uintN_t if you need an
unsigned version). Thinking of
int as a 32 bit integer and
long long as a 64 bit integer is just wrong. It might happen to be like this on your current platforms but this is relying on implementation-defined behavior.
Using fixed-width integer types is also useful for inter-operating with other technologies. For example, if some parts of your application are written in Java and others in C++, you'll probably want to match the integer types so you get consistent results. (Still be aware that overflow in Java has well-defined semantics while
signed overflow in C++ is undefined behavior so consistency is a high goal.) They will also be invaluable when exchanging data between different computing hosts.
If you don't need exactly N bits, but just a type that is wide enough, consider using
std::int_leastN_t (optimized for space) or
std::int_fastN_t (optimized for speed). Again, both families have
unsigned counterparts, too.
So, when to use the builtin types? Well, since the standard does not specify their width precisely, use them when you don't care about the actual bit width but about other characteristics.
char is the smallest integer that is addressable by the hardware. The language actually forces you to use it for aliasing arbitrary memory. It is also the only viable type for representing (narrow) character strings.
int will usually be the fastest type the machine can handle. It will be wide enough such that it can be loaded and stored with a single instruction (without having to mask or shift bits) and narrow enough so it can be operated on with (the most) efficient hardware instructions. Therefore,
int is a perfect choice for passing data and doing arithmetic when overflow is not a concern. For example, the default underlying type of enumerations is
int. Don't change it to a 32 bit integer just because you can. Also, if you have a value that can only be –1, 0 and 1, an
int is a perfect choice, unless you're going to store huge arrays of them in which case you might wish to use a more compact data type at the cost of having to pay a higher price for accessing individual elements. More efficient caching will likely pay off for these. Many operating system functions are also defined in terms of
int. It would be silly to convert their arguments and results back and forth. All this could possibly do is introduce overflow errors.
long will usually be the widest type that can be handled with single machine instructions. This makes especially
unsigned long very attractive for dealing with raw data and all kinds of bit manipulation stuff. For example, I would expect to see
unsigned long in the implementation of a bit-vector. If the code is written carefully, it doesn't matter how wide the type actually is (because the code will adapt automatically). On platforms where the native machine-word is 32 bit, having the backing array of the bit-vector be an array of
unsigned 32 bit integers is most desirable because it would be silly to use a 64 bit type that has to be loaded via expensive instructions only to shift and mask the unneeded bits away again anyway. On the other hand, if the platform's native word size is 64 bit, I want an array of that type because it means that operations like “find first set” may run up to twice as fast. So the “problem” of the
long data type that you're describing, that its size varies from platform to platform, actually is a feature that can be put to good used. It only becomes a problem if you think about the builtin types as types of a certain bit width, which they simply ain't.
long are very useful types as described above.
long long are not nearly as useful because their semantics are much less clear.