Most built-in bitwise operators (~, >>, >>=, &, &=,
^, ^=, |, and |=) have implementation-dependent results when performed on signed operands, and
bitwise left shift (<< and <<=) has unspecified or undefined behavior when performed on negative operands.
Therefore, bitwise operations should not be performed on signed operands.
Starting with C++20, the behaviors have been defined more accurately (negative values have to be represented using two’s complement), and therefore
this rule will only report an issue when the second operand of a shift operator is signed (shifting by a negative value is still undefined
behavior).
Noncompliant code example
if ( ( uint16_a & int16_b ) == 0x1234U ) // Noncompliant until C++20
if ( ~int16_a == 0x1234U ) // Noncompliant until C++20
auto f(int i) {
return 1 << i; // Noncompliant
}
Compliant solution
if ( ( uint16_a | uint16_b ) == 0x1234U )
if ( ~uint16_a == 0x1234U )
auto f(unsigned int i) {
return 1 << i;
}
Exceptions
When used as bit flags, it is acceptable to use preprocessor macros as arguments to the & and | operators even if the value is not explicitly
declared as unsigned.
fd = open(file_name, UO_WRONLY | UO_CREAT | UO_EXCL | UO_TRUNC, 0600);
If the right-side operand to a shift operator is known at compile time, it is acceptable for the value to be represented with a signed type
provided it is positive.
#define SHIFT 24
foo = 15u >> SHIFT;
When combining several bitwise operations, even if all leaf operands are unsigned, if they are smaller than an int, some intermediate
results will be of type signed int, due to integral promotion. However, this situation is usually not an issue, and is an exception for
this rule:
unsigned int f(unsigned short src) {
return (src >> 3) & 0x1F; // (src >> 3) is of type signed int
}