Selama ini kita hanya memakai satu bentuk parameter template: typename T. Padahal daftar di dalam template <...> jauh lebih kaya. Ada kata kunci saingan bernama class, ada kemampuan memberi nilai bawaan seperti parameter fungsi, dan — paling menarik — ada parameter yang berisi template lain. Chapter ini merapikan ketiganya dengan satu file contoh, karena ketiganya akan muncul terus-menerus di standard library: std::vector memakai default argument, dan container adaptor seperti std::stack memakai template template parameter di deklarasi aslinya.
typename atau class: Dua Kata, Satu Makna #
Kedua kata kunci berarti sama persis dalam daftar parameter template. Baris 6-9 di bawah memakai template <class T> dan berperilaku identik dengan template <typename T> — tidak ada perbedaan semantik, tidak ada perbedaan performa. T di sana bukan class dalam arti OOP; ia bisa int, double, enum, pointer, tipe apa pun. class di posisi ini hanyalah warisan sejarah: kata itu lebih dulu ada, typename ditambahkan belakangan karena class terasa menyesatkan untuk tipe non-class.
#include <iostream>
#include <string>
#include <vector>
// "class" dalam daftar parameter template identik dengan "typename"
template <class T>
T kuadrat(T x) {
return x * x;
}
// Default template argument: bila Container tak disebut, pakai std::vector<T>
template <typename T, typename Container = std::vector<T>>
class Tumpukan {
public:
void taruh(const T& nilai) { data_.push_back(nilai); }
T ambil() {
T nilaiAtas = data_.back();
data_.pop_back();
return nilaiAtas;
}
bool kosong() const { return data_.empty(); }
private:
Container data_;
};
// Template dengan satu parameter tipe, siap diselipkan ke template lain
template <typename T>
class Kotak {
public:
explicit Kotak(const T& isi) : isi_{isi} {}
const T& isi() const { return isi_; }
private:
T isi_;
};
template <typename T>
class LabelHarga {
public:
explicit LabelHarga(const T& nilai) : nilai_{nilai} {}
const T& isi() const { return nilai_; }
private:
T nilai_;
};
// Template template parameter: C adalah template lain (satu parameter tipe)
template <typename T, template <typename> class C = Kotak>
class Penyimpan {
public:
explicit Penyimpan(const T& isi) : kotak_{isi} {}
void tampilkan() const { std::cout << "disimpan: " << kotak_.isi() << "\n"; }
private:
C<T> kotak_;
};
int main() {
std::cout << "kuadrat(6) = " << kuadrat(6) << "\n";
Tumpukan<std::string> tumpukan; // Container default: std::vector<std::string>
tumpukan.taruh("Budi");
tumpukan.taruh("Siti");
std::cout << "ambil: " << tumpukan.ambil() << "\n";
Penyimpan<int> bawaan{7}; // C default: Kotak
bawaan.tampilkan();
Penyimpan<int, LabelHarga> lain{99}; // C diganti: LabelHarga
lain.tampilkan();
return 0;
}Output yang Dihasilkan:
kuadrat(6) = 36
ambil: Siti
disimpan: 7
disimpan: 99Bedah singkatnya:
- Baris 6-9 (
template <class T> T kuadrat): satu-satunya tempatclassdipakai sebagai penggantitypenamedi file ini. Konvensi komunitas modern cenderung memakaitypenamekarena lebih jujur — ikuti saja satu gaya secara konsisten dalam satu proyek. - Baris 12 (
typename Container = std::vector<T>): parameter kedua punya nilai bawaan. PemanggilanTumpukan<std::string>pada baris 62 tidak menyebutContainer, sehingga compiler mengisinya denganstd::vector<std::string>. - Baris 49 (
template <typename T, template <typename> class C = Kotak>):Cadalah template template parameter — argumennya bukan tipe, melainkan template lain yang punya tepat satu parameter tipe. Baris 56 memakainya sebagaiC<T>. - Baris 70 (
Penyimpan<int, LabelHarga>):KotakdigantiLabelHarga. Selama template penggantinya punya satu parameter tipe dan menyediakanisi(), baris 53 tetap dikompilasi — inilah cikal bakal pola policy-based design yang kita tulis di akhir Section.
Karena Tumpukan diinstansiasi dengan std::string, urutan taruh lalu ambil pada baris 63-65 menghasilkan perilaku LIFO: nilai terakhir yang masuk (“Siti”) keluar duluan.
Default Template Argument #
Nilai bawaan pada parameter template bekerja seperti default argument pada fungsi: bila pemanggil tidak mengisi, compiler mengisi. Tiga aturan praktisnya cukup diingat:
- Nilai bawaan boleh bergantung pada parameter sebelumnya —
std::vector<T>memakaiTyang dideklarasikan di kiriannya. - Setelah satu parameter berisi nilai bawaan, semua parameter sesudahnya sebaiknya juga punya nilai bawaan; pemanggil tidak bisa “melewati” parameter tengah karena argumen diisi berurutan.
- Standard library memakai pola ini di mana-mana:
std::vector<T, Allocator = std::allocator<T>>danstd::basic_string<CharT, Traits, Allocator>semuanya menyembunyikan parameter teknis di belakang nilai bawaan.
Satu catatan penting bila kita ingin meniru std::vector di posisi parameter template yang lain: std::vector punya dua parameter (tipe dan allocator), sehingga template template parameter yang memintanya harus menerima dua parameter pula. Detail pencocokannya tergolong lanjutan; cukup tahu bahwa container standard “sedikit lebih gemuk” daripada Kotak buatan kita.
Template Template Parameter: Template sebagai Argumen #
Mengapa fitur ini perlu ada? Karena kadang kita ingin menyuruh class: “pakai jenis penyimpanan ini” tanpa menyebut tipenya terlebih dahulu. Pada Penyimpan<int, Kotak>, keputusan cara menyimpan (Kotak) dipisahkan dari keputusan apa yang disimpan (int). Kalau kita hanya pakai parameter tipe biasa, kita terpaksa menulis Penyimpan<Kotak<int>> — tipe penyimpanan dan isi sudah dilekatkan sejak awal, dan class kita tidak bisa mengambil T sendiri.
Deklarasi template <typename> class C membaca: “C adalah nama yang merujuk template class dengan satu parameter tipe”. Di dalam badan class, C<T> menginstansiasi template itu dengan tipe kita. Pola ini persis yang dipakai std::stack: deklarasi aslinya berbunyi template <class T, class Container = std::deque<T>> class stack; — isi tumpukan diserahkan ke container lain yang bisa diganti pemakainya.
Dengan tipe, nilai bawaan, dan template sebagai parameter, daftar template <...> masih menyisakan satu jenis parameter yang belum kita sentuh: parameter yang isinya angka, bukan tipe. Chapter berikutnya membahas non-type template parameter — mesin di balik std::array<int, 5> — dan cara compiler memperlakukan nilai pada saat kompilasi.
Referensi resmi: cppreference — Template parameters and arguments dan cppreference — std::stack .