Associative containers place a strict weak ordering requirement on their key comparison predicates [ISO/IEC 14882-2014]. A strict weak ordering has the following properties:
- For all
x
:x < x == false
(irreflexivity) - For all
x
,y
:x < y != y < x
(asymmetry) - For all x,
y
,z
:x < y && y < z == x < z
(transitivity)
Providing an invalid ordering predicate for an associative container, or as a comparison criterion with the sorting algorithms, can result in erratic behavior or infinite loops [Meyers 01]. When an ordering predicate is required for an associative container or a generic STL algorithm, the predicate must meet the requirements for inducing a strict weak ordering.
Noncompliant Code Example
In this noncompliant code example, the std::set
object is created with a comparator that does not adhere to the strict weak ordering requirement. Specifically, it fails to return false for equivalent values. As a result, the behavior of iterating over the results from std::set::equal_range
results in unspecified behavior:
#include <functional> #include <iostream> #include <set> void f() { std::set<int, std::less_equal<int>> S{5, 10, 20}; for (auto R = S.equal_range(10); R.first != R.second; ++R.first) { std::cout << *R.first << std::endl; } }
Compliant Solution
This compliant solution uses the default comparator with std::set
instead of providing an invalid one:
#include <iostream> #include <set> void f() { std::set<int> S{5, 10, 20}; for (auto R = S.equal_range(10); R.first != R.second; ++R.first) { std::cout << *R.first << std::endl; } }
Noncompliant Code Example
In this noncompliant code example, the objects stored in the std::set
have an overloaded operator<
implementation, allowing the objects to be compared with std::less
. However, the comparison operation does not provide a strict weak ordering. Specifically, the values 1, N
and 1, M
can result in a situation where comp(x, y) == comp(y, x)
, failing the asymmetry requirements.
#include <iostream> #include <set> class S { int I, J; public: S(int I, int J) : I(I), J(J) {} friend bool operator<(const S &LHS, const S &RHS) { return LHS.I < RHS.I && LHS.J < RHS.J; } friend std::ostream &operator<<(std::ostream &OS, const S& O) { OS << "I: " << O.I << ", J: " << O.J; return OS; } }; void f() { std::set<S> T{S(1, 1), S(1, 2), S(2, 1)}; for (auto V : T) { std::cout << V << std::endl; } }
Compliant Solution
This compliant solution uses std::tie()
to properly implement the strict weak ordering operator<
predicate:
#include <iostream> #include <set> #include <tuple> class S { int I, J; public: S(int I, int J) : I(I), J(J) {} friend bool operator<(const S &LHS, const S &RHS) { return std::tie(LHS.I, LHS.J) < std::tie(RHS.I, RHS.J); } friend std::ostream &operator<<(std::ostream &OS, const S& O) { OS << "I: " << O.I << ", J: " << O.J; return OS; } }; void f() { std::set<S> T{S(1, 1), S(1, 2), S(2, 1)}; for (auto V : T) { std::cout << V << std::endl; } }
Risk Assessment
Using an invalid ordering rule can lead to erratic behavior or infinite loops.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
CTR40-CPP | Low | Probable | High | P2 | L3 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
The CERT Oracle Coding Standard for Java | MET10-J. Follow the general contract when implementing the compareTo() method |
Bibliography
[ISO/IEC 14882-2014] | 23.2.4, "Associative Containers" |
[Meyers 01] | Item 21: Always have comparison functions return false for equal values |
[Sutter 05] | Item 83: Use a checked STL implementation |