Why is this an issue?
Comparison between signed
and unsigned
integers is dangerous because it produces counterintuitive results outside of
their common range of values.
When a signed integer is compared to an unsigned one, the former might be converted to unsigned. The conversion preserves the two’s-complement bit
pattern of the signed value that often corresponds to a large unsigned result. For example, 2U < -1
is true
.
C++20 introduced remedy to this common pitfall: a family of std::cmp_*
functions defined in the <utility>
header.
These functions correctly handle negative numbers and lossy integer conversion. For example, std::cmp_less(2U, -1)
is
false
.
This rule starts by detecting comparisons between signed and unsigned integers. Then, if the signed value can be proven to be negative, the rule
S6214 will raise an issue (it is a bug). Otherwise, this rule will raise an issue. Therefore, if this rule is enabled, S6214
should be enabled too.
Noncompliant code example
bool less = 2U < -1; // Compliant, raises S6214
bool foo(unsigned x, signed y) {
return x < y; // Noncompliant: y might be negative
}
bool fun(int x, std::vector<int> const& v) {
return x < v.size(); // Noncompliant: x might be negative
}
Compliant solution
bool less = std::cmp_less(2U, -1); // Compliant for this rule and S6214
bool foo(unsigned x, signed y) {
return std::cmp_less(x, y); // Compliant
}
bool fun(int x, std::vector<int> const& v) {
return std::cmp_less(x, v.size()); // Compliant
}
void compute(std::vector<int> const &v) {
if (0 < v.size() && v.size() < 100) { // Compliant, even though v.size() returns an unsigned integer
}
}
Resources
- S845 - a more generic rule about mixing signed and unsigned values.
- S6214 - a version of this rule that only triggers when it detects negative values are involved.