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.

cpp
#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:

text
int    : 7
double : 2.5
string : jeruk
eksplisit : 4.5

Mari kita telaah apa yang terjadi pada baris-baris kode di atas:

  1. 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 mengganti T.
  2. Baris 12 (maks(3, 7)): kedua argumen bertipe int, sehingga compiler menginstansiasi maks<int> dan mengganti setiap T menjadi int. Baris 13 dan 14 mengulang proses yang sama untuk double dan std::string — tiga fungsi berbeda lahir dari satu definisi.
  3. Baris 17 (maks<double>(3, 4.5)): argumen eksplisit <double> memaksa tipe T menjadi double; argumen 3 dikonversi ke 3.0. Tanpa petunjuk ini, T menerima dua instruksi yang bertentangan (int dari argumen pertama, double dari kedua) dan kompilasi ditolak.
  4. Baris 17 juga menunjukkan syarat template: badan template memakai operator>, sehingga T harus bertipe yang mendukung operator itu. Pemanggilan dengan tipe tanpa operator> 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 T dari beberapa argumen harus menghasilkan satu jawaban. maks(3, 4.5) menolak karena int dan double saling bertentangan; pemanggil bisa memilih maks<double>(3, 4.5) atau mengonversi argumen sebelum dipanggil.
  • Tipe hasil mengikuti T. Return type T berarti hasil maks<int> bertipe int, sehingga pembagian dua int di 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:

AlatWaktu kerjaCocok untuk
Overloadingcompiler memilih di antara fungsi tulisan tanganlogika yang memang berbeda per tipe
Lambda generic (auto)instansiasi per pemanggilan, lokal & ringkaskriteria singkat yang hidup di dekat pemanggilnya
Function templateinstansiasi per pemanggilan, dapat dideklarasikan di headerlogika 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>.