This is not possible if you want to remain fully portable.
The range of unsigned int
is only specified to at least cover the non-negative values of int
. The standard allows for implementations where UINT_MAX == INT_MAX
. The same applies to all other non-fixed-width integer types.
Given that the range of unsigned int
may be smaller than that of int
, the pigeonhole principle applies: you have no way of redistributing all values of int
to corresponding but different values of unsigned int
unless unsigned int
can store at least as many different values as int
.
To quote N4140 (roughly C++14):
3.9.1 Fundamental types [basic.fundamental]
1 [...] For narrow character types, all bits of the object representation participate in the value representation. For unsigned narrow character types, all possible bit patterns of the value representation represent numbers. These requirements do not hold for other types. [...]
3 For each of the standard signed integer types, there exists a corresponding (but different) standard unsigned integer type: "unsigned char
", "unsigned short int
", "unsigned int
", "unsigned long int
", and "unsigned long long int
", each of which occupies the same amount of storage and has the same alignment requirements (3.11) as the corresponding signed integer type47; that is, each signed integer type
has the same object representation as its corresponding unsigned integer type. [...] The range of non-negative values of a signed integer type is a
subrange of the corresponding unsigned integer type, and the value representation of each corresponding signed/unsigned type shall be the same. [...]
This guarantees that you don't have a problem for unsigned char
. There is no possibility for unsigned char
to have any kind of padding bits. It wouldn't make sense for unsigned char
to have padding bits: given unsigned char c;
, how would you access those padding bits? reinterpret_cast<unsigned char &>(c)
? That obviously just gives you c
. The only thing similar to padding bits that is possible for unsigned char
is something that's completely transparent to the program, for instance when ECC memory is used.
For all the other non-fixed-width integer type, from short
to long long
, the standard meaning of "subrange" allows for an equal range.
I think I vaguely recall reading that there may have been ancient CPUs that did not provide any native unsigned operations. This would make it very tricky for implementations to properly implement unsigned division, unless they declared that the would-be-sign-bit of unsigned types would be treated as a padding bit. This way, they could simply use the CPU's signed division instruction for either signed or unsigned types.