Reputation: 131544
Fundamental types in C++ have sizes between 1 and 8 (perhaps 16), on 64-bits operating systems. That means the number of bits they take up in memory is no higher than 128, i.e. the number of bits fits in an uint8_t
.
Now, suppose I write some function which takes such a number of bits. For example, suppose it's
template <typename T>
inline void clear_bit(T& x, magic_type bit_index ) {
static_assert(std::is_fundamental_v<T>, "Go away.");
x = x & ~(T{1} << bit_index);
}
I'm wondering what to use for magic_type
: Should it be a uint8_t
? Or maybe it should be just be an int, since I would need to check for validity anyway, even for a uint8_t
case, and with int
being a more "natural" type for numbers?
To make this question less about your individual opinion: Is one of the options generally considered more idiomatic? If not, can you find good enough reasons for both choices? Or perhaps suggest a third one?
Upvotes: 2
Views: 162
Reputation: 828
Whatever type you use for bit_index
in the expression T{1} << bit_index
, it will be promoted to int
or unsigned int
in any case and the result of T{1} << bit_index
itself is the promoted type of T{1}
. This means that x & ~(T{1} << bit_index)
always yield a type that is at least 'as large as' an int
.
The expression T{1} << bit_index
is well defined only if bit_index >= 0 && bit_index < (sizeof +T{}) * CHAR_BIT)
. The subsequent assignment to x
may still truncate the result.
As a rule of thumb, if you intend to use a variable in arithmetic expressions, use int
or unsigned int
. unsigned
is usually preferable in bitwise arithmetic contexts, otherwise just use signed
.
Bottom line: Use unsigned int
, or possibly signed int
.
Upvotes: 3