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 for all
x
:x < x == false
(irreflexivity) - For for all
x
,y
: ifx < y
then!(y < x)
(asymmetry) - For for all
x
,y
,z
: ifx < y && y < z
thenx < z
(transitivity)
Providing an invalid ordering predicate for an associative container (eg e.g., sets, maps, multisets, and multimaps), 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 standard template library algorithm, the predicate must meet the requirements for inducing a strict weak ordering.
...
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:.
Code Block | ||||
---|---|---|---|---|
| ||||
#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; } } |
...
This compliant solution uses the default comparator with std::set
instead of providing an invalid one:.
Code Block | ||||
---|---|---|---|---|
| ||||
#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; } } |
...
This compliant solution uses std::tie()
to properly implement the strict weak ordering operator<
predicate:.
Code Block | ||||
---|---|---|---|---|
| ||||
#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; } } |
...