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:

cpp
#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.

cpp Pola comparator yang salah
// 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:

cpp
#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).