Selama beberapa chapter terakhir kita sesekali menulis std::greater<int>{} dan std::multiplies<int>{} — objek yang dipanggil seperti fungsi. Pola itu bernama function object atau functor: class (atau struct) yang mendefinisikan operator(), sehingga objeknya bisa dipanggil dengan sintaks fungsi. Functor adalah mesin di balik banyak fitur C++: comparator algoritma, predikat, hash pada unordered_map, hingga deleter pada smart pointer. Berbeda dengan lambda yang sekali dibuat tidak punya nama tipe yang mudah disebut, functor adalah tipe sungguhan yang bisa disimpan, diuji, dan diwariskan. Chapter ini membedah cara membuatnya, kemampuan khasnya menyimpan state, function object bawaan standar, dan kapan functor lebih tepat daripada lambda.

Functor: Class dengan operator() #

Membuat functor tidak butuh fitur baru: kita sudah menguasai class, member function, dan constructor. Satu-satunya hal khusus adalah nama fungsi yang disewakan, operator(). Program berikut membuat functor BatasLulus yang dipakai sebagai predikat count_if:

cpp
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

// Functor: class dengan operator() sehingga objeknya bisa dipanggil
// seperti fungsi — dan bisa menyimpan state.
class BatasLulus {
public:
    explicit BatasLulus(int batas) : batas_(batas) {}

    bool operator()(int nilai) const {
        ++pemanggilan_;           // state: hitung berapa kali dipanggil
        return nilai >= batas_;
    }

    int pemanggilan() const { return pemanggilan_; }

private:
    int batas_;
    mutable int pemanggilan_ = 0;
};

int main() {
    std::vector<int> nilai = {60, 85, 40, 90, 55};

    // Functor dipakai sebagai predikat count_if
    auto lulus75 = BatasLulus(75);
    auto n = std::count_if(nilai.begin(), nilai.end(), lulus75);
    std::cout << "Lulus (>=75): " << n << "\n";

    // Konfigurasi berbeda: objek functor lain, logika class yang sama
    auto lulus50 = BatasLulus(50);
    auto m = std::count_if(nilai.begin(), nilai.end(), lulus50);
    std::cout << "Lulus (>=50): " << m << "\n";

    // Stateful functor + std::ref: algoritma menerima predikat BY VALUE,
    // jadi tanpa std::ref, state di dalam algoritma tidak terlihat di luar.
    BatasLulus penghitung(0);
    auto k = std::count_if(nilai.begin(), nilai.end(), std::ref(penghitung));
    std::cout << "Lulus (>=0) : " << k << "\n";
    std::cout << "Dipanggil   : " << penghitung.pemanggilan() << " kali\n";

    // Function object bawaan: std::greater sebagai comparator sort
    std::vector<int> data = {30, 10, 50, 20, 40};
    std::sort(data.begin(), data.end(), std::greater<int>{});
    std::cout << "Sort menurun: ";
    for (std::size_t i = 0; i < data.size(); ++i) {
        if (i > 0) std::cout << ' ';
        std::cout << data[i];
    }
    std::cout << "\n";
    return 0;
}#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

// Functor: class dengan operator() sehingga objeknya bisa dipanggil
// seperti fungsi — dan bisa menyimpan state.
class BatasLulus {
public:
    explicit BatasLulus(int batas) : batas_(batas) {}

    bool operator()(int nilai) const {
        ++pemanggilan_;           // state: hitung berapa kali dipanggil
        return nilai >= batas_;
    }

    int pemanggilan() const { return pemanggilan_; }

private:
    int batas_;
    mutable int pemanggilan_ = 0;
};

int main() {
    std::vector<int> nilai = {60, 85, 40, 90, 55};

    // Functor dipakai sebagai predikat count_if
    auto lulus75 = BatasLulus(75);
    auto n = std::count_if(nilai.begin(), nilai.end(), lulus75);
    std::cout << "Lulus (>=75): " << n << "\n";

    // Konfigurasi berbeda: objek functor lain, logika class yang sama
    auto lulus50 = BatasLulus(50);
    auto m = std::count_if(nilai.begin(), nilai.end(), lulus50);
    std::cout << "Lulus (>=50): " << m << "\n";

    // Stateful functor + std::ref: algoritma menerima predikat BY VALUE,
    // jadi tanpa std::ref, state di dalam algoritma tidak terlihat di luar.
    BatasLulus penghitung(0);
    auto k = std::count_if(nilai.begin(), nilai.end(), std::ref(penghitung));
    std::cout << "Lulus (>=0) : " << k << "\n";
    std::cout << "Dipanggil   : " << penghitung.pemanggilan() << " kali\n";

    // Function object bawaan: std::greater sebagai comparator sort
    std::vector<int> data = {30, 10, 50, 20, 40};
    std::sort(data.begin(), data.end(), std::greater<int>{});
    std::cout << "Sort menurun: ";
    for (std::size_t i = 0; i < data.size(); ++i) {
        if (i > 0) std::cout << ' ';
        std::cout << data[i];
    }
    std::cout << "\n";
    return 0;
}Lulus (>=75): 2
Lulus (>=50): 4
Lulus (>=0) : 5
Dipanggil   : 5 kali
Sort menurun: 50 40 30 20 10@@OUT:functor.cpp@@

Bedah baris demi baris:

1. **Baris 11-14 (`bool operator()(int nilai) const`)**: inilah "fungsi" milik objek. Dengan operator ini, `lulus75(60)` sah ditulis — seolah `lulus75` adalah fungsi — padahal ia objek yang memanggil metode miliknya.
2. **Baris 12 (`++pemanggilan_`)**: functor bisa menyimpan dan mengubah state miliknya. Kata `mutable` mengizinkan perubahan member meski `operator()` bertanda `const`.
3. **Baris 26-27**: satu functor dikonfigurasi batas `75` dan dipakai `count_if`; hasilnya dua elemen lolos.
4. **Baris 30-31**: class yang sama diinstansiasi ulang dengan batas `50` — tanpa menulis ulang logika, empat elemen lolos. Inilah penggunaan ulang yang lambat terlihat pada lambda.
5. **Baris 35-37 (`std::ref(penghitung)`)**: penghitung pemanggilan terbaca `5` *karena* kita mengirim `std::ref`. Tanpa itu, hasilnya `0` — jebakan yang dibahas di bawah.
6. **Baris 40-41**: `std::greater<int>{}` yang sudah kita pakai untuk sort ternyata functor juga — class kecil dengan `operator()(a, b)` yang mengembalikan `a > b`.

## Stateful Functor dan Jebakan By-Value

Keunggulan paling khas functor adalah state. Functor bisa menghitung berapa kali ia dipanggil, mengakumulasi statistik, atau membawa konfigurasi. Tetapi ada jebakan yang wajib diketahui sejak awal: algoritma STL menerima predikat dan comparator *by value*. Artinya, `count_if` bekerja pada salinan functor kita — perubahan state terjadi pada salinan itu dan hilang saat algoritma selesai. Tanpa `std::ref`, `penghitung.pemanggilan()` akan tetap `0` walau algoritma berjalan lima kali.

Solusinya seperti pada program: bungkus dengan `std::ref(penghitung)` dari header `<functional>`, sehingga yang disalin algoritma hanyalah pembungkus referensi, dan setiap pemanggilan menyentuh objek asli. Aturan praktisnya sederhana: functor *stateless* dikirim langsung; functor yang state-nya perlu dibaca setelah algoritma selesai dikirim lewat `std::ref`.

> [!IMPORTANT]
> Kesadaran ini juga menjelaskan mengapa predikat `count_if` standar menuntut predikat yang "murni": bila hasil tergantung state yang berubah, perilaku antar implementasi bisa berbeda karena algoritma bebas menyalin predikat sebanyak yang ia mau.

## Function Object Bawaan dari <functional>

Standar menyediakan deretan functor siap pakai di header `<functional>`, sehingga kebutuhan umum tidak perlu ditulis sendiri:

| Functor | Bisa dipanggil sebagai | Kegunaan umum |
|---|---|---|
| `std::less<T>` | `less(a, b)` → `a < b` | comparator default `std::sort`, `std::map` |
| `std::greater<T>` | `greater(a, b)` → `a > b` | sort menurun, priority queue terbesar dulu |
| `std::plus<T>` | `plus(a, b)` → `a + b` | operasi default `accumulate` |
| `std::multiplies<T>` | `multiplies(a, b)` → `a * b` | produk pada `accumulate`, `partial_sum` |
| `std::negate<T>` | `negate(a)` → `-a` | operasi unary pada `transform` |

Pola `std::map<K, V, std::greater<K>>` untuk map yang terurut menurun, atau `std::priority_queue<T, std::vector<T>, std::greater<T>>` untuk antrean prioritas terkecil dulu, adalah pemakaian harian yang akan kita jumpai lagi di Section lanjutan.

## Functor vs Lambda: Kapan yang Mana

Lambda (yang kita pelajari di Section *Fungsi*) adalah cara ringkas membuat function object anonim — di balik layar compiler juga membentuk class dengan `operator()`. Karena itu pilihan keduanya bukan soal kecepatan runtime (identik), melainkan kejelasan desain. Pilih lambda bila logika pendek, sekali pakai, dan tertulis di tempat pemakaiannya. Pilih functor bila logika butuh nama yang bermakna, dipakai di beberapa file, punya konfigurasi lewat constructor, punya state yang perlu diinspeksi, atau butuh beberapa `operator()` overload — hal-hal yang pada lambda memaksa kita menyalin-tempel.

> [!TIP]
> Functor yang bagus biasanya kecil dan fokus: satu tugas, satu state. Bila `operator()` mulai bercabang panjang, itu tanda logikanya lebih pantas menjadi member function biasa atau fungsi bebas.

Dengan function objects menguasai perbandingan dan predikat, kita siap untuk gaya pengolahan data paling modern di C++: ranges dan views yang membungkus semuanya dalam pipeline yang malas dievaluasi.

Rujukan resmi: [header \<functional\>](https://en.cppreference.com/w/cpp/utility/functional) dan [std::greater](https://en.cppreference.com/w/cpp/utility/functional/greater).