Template umum memperlakukan semua tipe dengan aturan yang sama, tetapi kadang satu tipe butuh pengecualian. Kotak<bool> mungkin ingin menampilkan “benar/salah” alih-alih angka; Kotak<const char*> mungkin ingin menyalin teks ke std::string alih-alih menyimpan pointer mentah yang rawan menggantung. C++ menyediakan mekanisme full specialization: definisi pengganti yang dipakai compiler bila argumen template mencocokkan satu tipe pasti. Pratinjau singkatnya sudah kita lihat di awal Section — sekarang kita menulisnya utuh, mengujinya, dan menetapkan kapan alat ini layak diambil.

Full Specialization untuk Satu Tipe #

Sintaks kuncinya dua bagian: template <> (daftar parameter template kosong, karena tipenya sudah pasti) diikuti nama class yang sudah “diisi” argumennya, misalnya Kotak<bool>. Setelah itu badan class boleh ditulis bebas — tidak wajib meniru bentuk template umumnya.

cpp
#include <iostream>
#include <string>

// Template umum: cetak biru untuk semua tipe
template <typename T>
class Kotak {
public:
    explicit Kotak(const T& isi) : isi_{isi} {}
    const T& isi() const { return isi_; }
    void cetak() const { std::cout << "Kotak<T> berisi " << isi_ << "\n"; }

private:
    T isi_;
};

// Full specialization untuk bool: perilaku khusus
template <>
class Kotak<bool> {
public:
    explicit Kotak(bool isi) : isi_{isi} {}
    bool isi() const { return isi_; }
    void cetak() const {
        std::cout << "Kotak<bool> berisi " << (isi_ ? "benar" : "salah") << "\n";
    }

private:
    bool isi_;
};

// Full specialization untuk const char*: simpan salinan aman sebagai std::string
template <>
class Kotak<const char*> {
public:
    explicit Kotak(const char* isi) : isi_{isi} {}
    const std::string& isi() const { return isi_; }
    void cetak() const {
        std::cout << "Kotak<const char*> menyimpan salinan: " << isi_ << "\n";
    }

private:
    std::string isi_;
};

int main() {
    Kotak<int> angka{42};
    Kotak<bool> bendera{true};
    Kotak<const char*> teks{"Siti"};

    angka.cetak();
    bendera.cetak();
    teks.cetak();

    std::cout << "bendera: " << bendera.isi() << "\n";
    std::cout << "panjang teks: " << teks.isi().size() << "\n";

    return 0;
}

Output yang Dihasilkan:

text
Kotak<T> berisi 42
Kotak<bool> berisi benar
Kotak<const char*> menyimpan salinan: Siti
bendera: 1
panjang teks: 4

Bedah baris demi baris:

  1. Baris 5-14: template umum yang berlaku untuk semua tipe kecuali yang punya spesialisasi. Kotak<int> angka{42} pada baris 45 jatuh ke sini.
  2. Baris 17-18 (template <> class Kotak<bool>): full specialization pertama. Bagi compiler, Kotak<bool> kini adalah class berdiri sendiri — boleh punya member, constructor, dan data member yang berbeda dari template umumnya.
  3. Baris 22-24: cetak() versi bool menampilkan teks “benar/salah” lewat conditional operator, bukan angka. Inilah “perilaku khusus per tipe” yang dimaksud.
  4. Baris 31-32 (template <> class Kotak<const char*>): full specialization kedua. Data membernya diubah menjadi std::string (baris 41) — keputusan penyimpanan yang berbeda dari template umum.
  5. Baris 47 dan 54: pemanggil teks.isi().size() bekerja karena versi spesialisasi mengembalikan const std::string& — antarmuka yang lebih kaya dibanding pointer mentah.
  6. Baris 53: bendera.isi() mengembalikan bool, sehingga tercetak 1 tanpa std::boolalpha.

Kapan Spesialisasi Layak Dipakai #

Spesialisasi bukan alat sehari-hari, tetapi tiga situasi berikut sering muncul di kode profesional:

  • Optimasi per tipe. Template umum berjalan benar untuk semua tipe, namun satu tipe punya algoritme yang jauh lebih cepat. Standard library melakukannya di banyak tempat, misalnya versi std::copy untuk tipe yang bisa disalin mentah dengan memcpy.
  • Penyelamatan tipe bermasalah. Contoh di atas: const char* tidak punya semantik kepemilikan, jadi spesialisasi mengubahnya menjadi std::string yang aman. Pola serupa: menyimpan bool secara hemat dengan bit, atau menangani void yang menolak sebagian operasi.
  • Kunci ke standard library. Class buatan kita yang ingin jadi key pada std::unordered_map butuh std::hash<NamaKita> — dan cara resminya adalah full specialization dari template std::hash di namespace std (satu dari sedikit spesialisasi yang diizinkan standar di sana).

Spesialisasi Function Template #

Function template juga bisa dispesialisasi penuh. Kita kembali ke maks dari chapter pertama: untuk const char*, perbandingan > membandingkan alamat memori — hasilnya ditentukan posisi literal di memori, bukan urutan alfabet. Versi khusus memakai std::strcmp:

cpp
#include <cstring>
#include <iostream>

template <typename T>
T maks(T a, T b) {
    return (a > b) ? a : b;
}

// Full specialization function template untuk const char*:
// bandingkan isi teks, bukan alamat memori
template <>
const char* maks<const char*>(const char* a, const char* b) {
    return std::strcmp(a, b) > 0 ? a : b;
}

int main() {
    std::cout << "angka : " << maks(3, 9) << "\n";
    std::cout << "string: " << maks("jeruk", "apel") << "\n";

    const char* hasil = maks<const char*>("kiwi", "mangga");
    std::cout << "eksplisit: " << hasil << "\n";

    return 0;
}

Output yang Dihasilkan:

text
angka : 9
string: jeruk
eksplisit: mangga

Yang perlu dicermati:

  1. Baris 11-14 (template <> const char* maks<const char*>(...)): bentuknya dekat dengan overloading biasa, tetapi template <> dan nama maks<const char*> menandakan ini spesialisasi — versi dari template yang sudah ada, bukan fungsi baru.
  2. Baris 18: pemanggilan maks("jeruk", "apel") tidak menyebut argumen template apa pun, namun compiler tetap memilih spesialisasi: argumen const char[6] meluruh menjadi const char*, dan pencarian menemukan spesialisasi untuk T = const char*.
  3. Baris 20: argumen eksplisit maks<const char*> juga sah dan menghasilkan pemilihan yang sama. Hasilnya “mangga” — urutan alfabet, bukan alamat memori.

Satu catatan gaya: untuk function template, banyak programmer memilih overloading biasa (const char* maks(const char*, const char*)) karena aturan pemilihannya lebih sederhana dan tidak mudah salah urutan. Spesialisasi function template punya jebakan tersendiri yang berhubungan dengan overloading — dan karena function template sama sekali tidak mendukung partial specialization, pilihan alternatifnya menjadi penting. Kita bongkar semuanya pada Chapter Partial Specialization.

Referensi resmi: cppreference — Template specialization dan cppreference — std::hash .