Overloading menyelamatkan kita ketika logika sama harus ditulis untuk beberapa tipe, tetapi menulis int maks(int, int), double maks(double, double), dan std::string maks(std::string, std::string) satu per satu tetap pekerjaan yang berulang. Function template menghilangkan pengulangan itu: kita mendefinisikan satu cetak biru fungsi dengan tipe sebagai parameter, dan compiler menghasilkan versi yang tepat setiap kali template dipanggil dengan tipe tertentu. Mekanisme inilah fondasi seluruh standard library — std::sort, std::max, std::vector semuanya lahir dari template. Pada Chapter ini kita menulis template pertama dan memahami cara compiler mendeduksi tipenya.
Template Pertama: maks #
Deklarasi template diawali template <typename T> diikuti definisi fungsi biasa yang memakai T sebagai nama tipe. T bukan tipe sebenarnya — ia penanda yang diganti compiler pada setiap instansiasi.
#include <iostream>
#include <string>
template <typename T>
T maks(T a, T b) {
return (a > b) ? a : b;
}
int main() {
std::cout << "int : " << maks(3, 7) << "\n";
std::cout << "double : " << maks(2.5, 1.8) << "\n";
std::cout << "string : " << maks(std::string("apel"), std::string("jeruk")) << "\n";
// deduksi dari argumen berbeda = error kompilasi:
// maks(3, 4.5); // error: deduced conflicting types for 'T'
std::cout << "eksplisit : " << maks<double>(3, 4.5) << "\n";
return 0;
}Output yang Dihasilkan:
int : 7
double : 2.5
string : jeruk
eksplisit : 4.5Mari kita telaah apa yang terjadi pada baris-baris kode di atas:
- Baris 5-8 (
template <typename T> T maks(T a, T b)): definisi ini bukan fungsi yang langsung dapat dipanggil — ia cetak biru. Compiler menunggu pemanggilan untuk menggantiT. - Baris 12 (
maks(3, 7)): kedua argumen bertipeint, sehingga compiler menginstansiasimaks<int>dan mengganti setiapTmenjadiint. Baris 13 dan 14 mengulang proses yang sama untukdoubledanstd::string— tiga fungsi berbeda lahir dari satu definisi. - Baris 17 (
maks<double>(3, 4.5)): argumen eksplisit<double>memaksa tipeTmenjadidouble; argumen3dikonversi ke3.0. Tanpa petunjuk ini,Tmenerima dua instruksi yang bertentangan (intdari argumen pertama,doubledari kedua) dan kompilasi ditolak. - Baris 17 juga menunjukkan syarat template: badan template memakai
operator>, sehinggaTharus bertipe yang mendukung operator itu. Pemanggilan dengan tipe tanpaoperator>akan ditolak compiler pada saat instansiasi.
Deduksi bekerja per pemanggilan: setiap kombinasi tipe yang berbeda menghasilkan instansiasi baru. Karena itu template tidak menambah biaya runtime — yang terjadi hanyalah lebih banyak fungsi yang dihasilkan pada saat kompilasi.
Deduksi Tipe dan Aturannya #
Aturan deduksi yang paling sering terasa: template mengurangi reference dan const top-level dari parameter, sehingga maks(x, y) dengan x dan y bertipe int menginstansiasi maks<int> meskipun parameter dideklarasi T a. Untuk kebutuhan awal, dua konsekuensi praktisnya cukup diingat:
- Dua argumen, satu tipe. Deduksi
Tdari beberapa argumen harus menghasilkan satu jawaban.maks(3, 4.5)menolak karenaintdandoublesaling bertentangan; pemanggil bisa memilihmaks<double>(3, 4.5)atau mengonversi argumen sebelum dipanggil. - Tipe hasil mengikuti T. Return type
Tberarti hasilmaks<int>bertipeint, sehingga pembagian duaintdi dalam template lain akan memakai aritmetika integer — perhatikan ini saat menyusun template aritmetika.
Bila satu parameter saja perlu fleksibel, dua parameter tipe juga sah: template <typename A, typename B> void cetakPasangan(A a, B b) menerima tipe campur tanpa konversi. Pola ini bebas dipakai sesuai kebutuhan; template tidak terbatas satu parameter tipe.
Template vs Overloading vs auto #
Tiga alat yang kita pelajari pada Section ini mengerjakan masalah yang mirip dengan cara berbeda, dan sering dipakai bersama:
| Alat | Waktu kerja | Cocok untuk |
|---|---|---|
| Overloading | compiler memilih di antara fungsi tulisan tangan | logika yang memang berbeda per tipe |
Lambda generic (auto) | instansiasi per pemanggilan, lokal & ringkas | kriteria singkat yang hidup di dekat pemanggilnya |
| Function template | instansiasi per pemanggilan, dapat dideklarasikan di header | logika yang dipakai ulang lintas file dan lintas proyek |
Template mengalahkan ketika definisi harus dipakai di banyak tempat; lambda generic mengalahkan untuk kebutuhan sekali pakai yang tetap di satu fungsi. Keduanya bekerja pada saat kompilasi — tidak ada pemilihan tipe yang terjadi saat program berjalan.
Chapter ini menutup Section Fungsi. Perjalanan kita kini telah lengkap dari fungsi paling sederhana sampai cetak biru generik yang menopang seluruh standard library. Section berikutnya, Array dan String, mengalihkan fokus ke penyimpanan banyak data — dan di sana template yang baru kita kenal akan muncul terus-menerus dalam bentuk std::vector<T> dan std::array<T, N>.