Custom comparators and sorting
Sort records and custom data using strict weak ordering, comparator functions, and lambdas.
Learning objectives
- Write a comparator for custom records.
- Explain the strict weak ordering requirement.
- Choose between
sortandstable_sort. - Sort by multiple fields without fragile comparison logic.
Prerequisites: understand std::vector, iterators, functions, and basic lambda expressions. Review Vectors, STL algorithms, and Lambda expressions first. Examples use C++17.
Sorting simple values
#include <algorithm>
#include <functional>
#include <vector>
std::vector<int> numbers{8, 3, 6, 1};
std::sort(numbers.begin(), numbers.end()); // ascending
std::sort(numbers.begin(), numbers.end(), std::greater<int>{}); // descendingInclude <algorithm> for sorting and <functional> for standard function objects such as std::greater.
What a comparator means
A comparator answers: should the left element appear before the right element? It does not answer whether the values are equal. For ascending order, a < b is a suitable comparison.
#include <algorithm>
#include <string>
#include <vector>
struct Student {
std::string name;
int score;
};
std::vector<Student> students{{"Ada", 95}, {"Linus", 88}};
std::sort(students.begin(), students.end(),
[](const Student& a, const Student& b) {
return a.score > b.score; // higher score first
});
Strict weak ordering
Standard sorting algorithms require a strict weak ordering. For beginner use, remember three practical rules: an element must not compare before itself; comparisons must be consistent; and equivalence must behave consistently. Do not use <= or >= as a sorting comparator because they say an element comes before itself.
Sort by multiple fields
std::sort(students.begin(), students.end(),
[](const Student& a, const Student& b) {
if (a.score != b.score) return a.score > b.score;
return a.name < b.name;
});This orders higher scores first and breaks ties by name in ascending order. The comparator uses the second field only when the first field ties.
Using std::tie
#include <tuple>
std::sort(students.begin(), students.end(),
[](const Student& a, const Student& b) {
return std::tie(a.score, a.name) > std::tie(b.score, b.name);
});This example sorts both fields in descending order. If you need mixed directions, use explicit comparisons so the intent stays clear.
sort versus stable_sort
std::sort is not stable: equivalent elements may change relative order. std::stable_sort preserves their original relative order. Both perform O(n log n) comparisons; stable_sort may use extra memory.
Common mistakes
- Using
a.score >= b.scoreand violating strictness. - Forgetting a tie-breaker when deterministic ordering matters.
- Writing a comparator whose result changes while sorting.
- Assuming
sortpreserves the order of equal elements.
Practice
- Sort words by length, then alphabetically.
- Sort products by price ascending and rating descending.
- Explain why
return a <= b;is invalid for sorting. - When would
stable_sortbe useful?
Show solutions
- Compare lengths first; if equal, compare strings with
<. - Compare price first; for equal prices, compare rating with
>. - It reports an element as ordered before itself when both arguments are equal.
- When the original order among equivalent elements carries useful meaning.
Summary
Comparators define the order expected by sorting algorithms. Keep them consistent and strict, and use explicit tie-breaking for multi-field records.