Sejak Chapter Sorting dengan std::sort kita sesekali menyisipkan argumen ketiga pada std::sort — comparator — tanpa membahasnya secara tuntas. Comparator adalah fungsi atau objek yang bisa dipanggil dengan dua argumen, comp(a, b), yang mengembalikan true bila a harus ditempatkan sebelum b. Praktisnya, comparator menggantikan operator< pada urutan apa pun yang kita mau: menurun, berdasarkan field tertentu dari struct, atau kombinasi beberapa kunci sekaligus. Tidak hanya std::sort — min_element, max_element, binary_search, lower_bound, dan partition juga menerima comparator. Chapter ini membahas tiga bentuk penulisannya, syarat yang wajib dipenuhi (strict weak ordering), dan pola multi-kriteria yang sering dibutuhkan di dunia nyata.
Tiga Bentuk Comparator #
Comparator bisa ditulis sebagai fungsi bebas, lambda, atau function object. Ketiganya setara bagi algoritma — yang membedakan adalah keterbacaan, penggunaan ulang, dan kebutuhan state. Program berikut menyortir dengan urutan menurun lewat ketiga bentuk tersebut:
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
void cetak(const std::vector<int>& v) {
for (std::size_t i = 0; i < v.size(); ++i) {
if (i > 0) std::cout << ' ';
std::cout << v[i];
}
std::cout << '\n';
}
// Bentuk 1: comparator sebagai fungsi bebas
bool menurun(int a, int b) {
return a > b;
}
int main() {
std::vector<int> data = {30, 10, 50, 20, 40};
std::vector<int> v1 = data;
std::sort(v1.begin(), v1.end(), menurun); // fungsi bebas
std::cout << "Fungsi bebas : ";
cetak(v1);
std::vector<int> v2 = data;
std::sort(v2.begin(), v2.end(),
[](int a, int b) { return a > b; }); // lambda
std::cout << "Lambda : ";
cetak(v2);
std::vector<int> v3 = data;
std::sort(v3.begin(), v3.end(), std::greater<int>{}); // function object
std::cout << "greater<int> : ";
cetak(v3);
return 0;
}Bila a == b, kedua arah comp(a, b) dan comp(b, a) bernilai true — melanggar asimetris. Algoritma seperti quicksort bisa melampaui batas rentang dan menyebabkan undefined behavior, mulai dari hasil tidak terurut sampai crash. Pada g++ 14.2, kode di atas dikompilasi tanpa protes karena compiler tidak memeriksa makna logika comparator; tanggung jawabnya ada pada kita.
// SALAH — melanggar irrefleksif, hasil sort tidak terdefinisi
std::sort(v.begin(), v.end(), [](int a, int b) { return a <= b; });Menyortir struct dengan Multi-Kriteria #
Kasus dunia nyata jarang se-sederhana satu kunci. Contoh berikut mengurutkan produk: kategori menaik, lalu stok menurun di dalam kategori yang sama, lalu nama menaik bila stoknya juga sama:
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
struct Produk {
std::string nama;
std::string kategori;
int stok;
};
int main() {
std::vector<Produk> gudang = {
{"Laptop", "Elektronik", 8},
{"Buku Tulis", "Alat Tulis", 120},
{"Monitor", "Elektronik", 15},
{"Pulpen", "Alat Tulis", 60},
{"Kalkulator", "Elektronik", 8},
};
// Multi-kriteria: kategori menaik, lalu stok menurun, lalu nama
std::sort(gudang.begin(), gudang.end(),
[](const Produk& a, const Produk& b) {
if (a.kategori != b.kategori) return a.kategori < b.kategori;
if (a.stok != b.stok) return a.stok > b.stok;
return a.nama < b.nama;
});
for (const Produk& p : gudang) {
std::cout << p.kategori << " | " << p.nama << " | stok " << p.stok
<< "\n";
}
return 0;
}#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
struct Produk {
std::string nama;
std::string kategori;
int stok;
};
int main() {
std::vector<Produk> gudang = {
{"Laptop", "Elektronik", 8},
{"Buku Tulis", "Alat Tulis", 120},
{"Monitor", "Elektronik", 15},
{"Pulpen", "Alat Tulis", 60},
{"Kalkulator", "Elektronik", 8},
};
// Multi-kriteria: kategori menaik, lalu stok menurun, lalu nama
std::sort(gudang.begin(), gudang.end(),
[](const Produk& a, const Produk& b) {
if (a.kategori != b.kategori) return a.kategori < b.kategori;
if (a.stok != b.stok) return a.stok > b.stok;
return a.nama < b.nama;
});
for (const Produk& p : gudang) {
std::cout << p.kategori << " | " << p.nama << " | stok " << p.stok
<< "\n";
}
return 0;
}Alat Tulis | Buku Tulis | stok 120
Alat Tulis | Pulpen | stok 60
Elektronik | Monitor | stok 15
Elektronik | Kalkulator | stok 8
Elektronik | Laptop | stok 8@@OUT:komparator-struct.cpp@@
Comparator di badan lambda memeriksa kriteria dari yang paling dominan. Baris pertama mengembalikan keputusan bila kategori berbeda; bila sama, baris kedua membandingkan stok secara menurun; bila stok juga sama, baris ketiga membandingkan nama. Pola "periksa berlapis, kembalikan secepat mungkin" ini mudah dikembangkan ke empat, lima kunci. Perhatikan hasilnya: `Laptop` dan `Kalkulator` sama-sama berkategori Elektronik dengan stok 8, lalu nama yang menentukan urutan mereka.
> [!TIP]
> Bila kunci pembandingnya mahal dihitung (misalnya string panjang atau hasil transformasi), simpan kuncinya dulu di struct terpisah, atau gunakan *projection* pada `std::ranges::sort` agar perbandingan langsung pada member tanpa menulis lambda panjang.
Dengan comparator, kita telah menyentuh "objek yang dipanggil seperti fungsi" berkali-kali. Chapter berikutnya membahasnya sebagai satu konsep utuh: function objects.
Rujukan resmi: [syarat Compare](https://en.cppreference.com/w/cpp/named_req/Compare) dan [std::sort](https://en.cppreference.com/w/cpp/algorithm/sort).