Section ini membuka babak baru: dari pengguna kode menjadi penulis kode generik. Pada Section Fungsi kita sudah membuat satu function template sederhana bernama maks — sekarang kita membongkarnya lebih dalam: template dengan beberapa parameter tipe, return type yang dideduksi compiler, dan sekilas kemampuan memberi perilaku khusus untuk tipe tertentu. Semua mekanisme ini bekerja penuh pada saat kompilasi, sehingga fleksibilitas yang kita dapat tidak menambah biaya apa pun saat program berjalan. Kuasai chapter ini dengan baik, karena empat belas chapter ke depan semuanya berdiri di atasnya.
Recap: Satu Definisi untuk Banyak Tipe #
Ingat kembali pola dasarnya. template <typename T> membuka daftar parameter template, lalu T dipakai sebagai nama tipe di dalam signature fungsi. Compiler tidak mengompilasi template apa adanya; ia menunggu pemanggilan, mendeduksi T dari argumen, lalu menginstansiasi fungsi sebenarnya untuk tipe tersebut.
#include <iostream>
#include <string>
// Recap: satu parameter tipe T, persis seperti pengantar di Section Fungsi
template <typename T>
T maks(T a, T b) {
return (a > b) ? a : b;
}
// Pendalaman: dua parameter tipe, A dan B boleh berbeda
template <typename A, typename B>
void cetakPasangan(const A& pertama, const B& kedua) {
std::cout << "(" << pertama << ", " << kedua << ")\n";
}
// Return type tidak ditulis manual; compiler mendeduksinya dari return
template <typename A, typename B>
auto kali(A a, B b) {
return a * b;
}
int main() {
std::cout << "maks(10, 3) -> " << maks(10, 3) << "\n";
std::cout << "maks(string) -> " << maks(std::string("kiwi"), std::string("apel")) << "\n";
cetakPasangan(7, "tujuh");
cetakPasangan(2.5, 'X');
std::cout << "kali(3, 4) -> " << kali(3, 4) << "\n";
std::cout << "kali(3, 0.5) -> " << kali(3, 0.5) << "\n";
return 0;
}Output yang Dihasilkan:
maks(10, 3) -> 10
maks(string) -> kiwi
(7, tujuh)
(2.5, X)
kali(3, 4) -> 12
kali(3, 0.5) -> 1.5Mari kita bedah baris demi baris:
- Baris 5-8 (
T maks(T a, T b)): cetak biru yang sudah kita kenal. Pemanggilanmaks(10, 3)pada baris 23 menginstansiasimaks<int>, sedangkan baris 24 menginstansiasimaks<std::string>— dua fungsi berbeda dari satu definisi. Perbandingan string menghasilkankiwikarenaoperator>padastd::stringbekerja leksikografis. - Baris 11-14 (
void cetakPasangan(const A&, const B&)): template ini punya dua parameter tipe. PemanggilancetakPasangan(7, "tujuh")menduksiA = intdanB = const char*; tipe yang berbeda tidak lagi menjadi masalah karena masing-masing punya parameter sendiri. - Baris 18-20 (
auto kali(A a, B b)): return type tidak ditulis; compiler menyimpulkannya dari pernyataanreturn. Darikali(3, 4)lahir fungsi yang menghasilkanint, sedangkankali(3, 0.5)menghasilkandoublekarena3 * 0.5bertipedouble. - Baris 26-27: parameter template dikirim sebagai
const A&— pengiriman lewat reference menghindari penyalinan object besar, kebiasaan yang juga dipakai standard library.
Dua Parameter Tipe atau Lebih #
Daftar parameter template bisa memuat sebanyak mungkin parameter, dipisah koma, persis seperti daftar parameter fungsi. Setiap parameter harus terpakai dalam signature agar bisa dideduksi, atau diberi nilai eksplisit saat pemanggilan seperti kali<double, double>(3, 4). Dalam praktik, template dengan dua hingga tiga parameter tipe sangat lazim — std::pair misalnya punya dua — sedangkan yang lebih banyak biasanya menandakan desain yang patut ditinjau ulang.
Return type eksplisit juga tetap bisa ditulis bila kita ingin menentukan sendiri hasilnya. Alternatif modernnya adalah trailing return type: auto kali(A a, B b) -> decltype(a * b) memakai decltype untuk mendeklarasikan tipe hasil ekspresi a * b tanpa harus mengetahuinya di depan. Bentuk auto polos (C++14 ke atas) lebih ringkas dan cukup untuk kebanyakan kasus.
template <typename T>
auto pilih(T a, T b) {
if (a > b) return a;
return a * 1.5; // int lalu double — konflik
}
int main() {
pilih(3, 2); // instansiasi memicu deduksi return type
}Pesan yang keluar dari g++ 14.2:
konflik-auto.cpp: In instantiation of 'auto pilih(T, T) [with T = int]':
konflik-auto.cpp:8:10: required from here
8 | pilih(3, 2); // instansiasi memicu deduksi return type
| ~~~~~^~~~~~
konflik-auto.cpp:4:14: error: inconsistent deduction for auto return type: 'int' and then 'double'
4 | return a * 1.5; // int lalu double — konflik
| ~~^~~~~Perhatikan urutan laporannya: compiler menunjuk pemanggilan di main sebagai pemicu instansiasi, lalu titik persisnya konflik. Pola “required from here” seperti ini akan menemani kita sepanjang Section, jadi biasakan membacanya dari bawah ke atas.
Pratinjau: Spesialisasi Eksplisit #
Template menerima semua tipe secara setara, tetapi kadang satu tipe butuh perlakuan istimewa. Contohnya maks("jeruk", "apel"): dengan T = const char*, operator> membandingkan alamat memori, bukan isi teks. C++ menyediakan full specialization — definisi khusus untuk satu tipe pasti — yang ditulis dengan template <>:
#include <cstring>
template <typename T>
T maks(T a, T b) {
return (a > b) ? a : b;
}
// Versi khusus untuk const char*: bandingkan isi string
template <>
const char* maks(const char* a, const char* b) {
return std::strcmp(a, b) > 0 ? a : b;
}Sintaks ini sah dan bisa dikompilasi; compiler memilih versi khusus tersebut setiap kali maks dipanggil dengan dua const char*. Kita tidak membahas aturan lengkapnya sekarang — Chapter Template Specialization membuktikannya dengan kode yang diuji dan menunjukkan kapan spesialisasi layak dipakai.
Dengan rekapan tipe dan return type yang sudah rapat, kita siap naik satu tingkat: membuat bukan hanya fungsi, tetapi class yang generik. Chapter berikutnya membangun Kotak<T> — cetak biru class yang menjadi bahan latihan Section ini — dan mengenal std::vector<T> sebagai class template paling terkenal di dunia C++.
Referensi resmi: cppreference — Templates dan cppreference — Function template .