Sebuah Stack untuk int dan Stack untuk std::string sebenarnya memiliki logika identik — yang berbeda hanya tipe elemen. Menulis dua versi kode hanya karena tipe berbeda adalah duplikasi yang harus dihindari. C++ menyediakan template — mekanisme generic programming yang memungkinkan kita menulis kode sekali dan menggunakannya untuk berbagai tipe. Pada Chapter ini kita akan mempelajari function template dan class template, yang menjadi pondasi seluruh STL (vector, map, queue) sekaligus struktur data generik yang akan kita bangun sendiri.

Function Template #

Function template adalah blueprint yang dipakai compiler untuk menghasilkan fungsi konkret saat dipanggil. Kita menulis satu definisi dengan type parameter T, lalu compiler membuat instance untuk setiap tipe yang dipakai.

Mekanisme: Type Parameter dan Instantiation #

Saat kita menulis template <typename T> T maksimum(T a, T b), compiler belum menghasilkan kode apapun. Ketika pemanggilan maksimum(3, 7) terjadi, compiler menyimpulkan T = int dan menghasilkan instance int maksimum(int, int). Pemanggilan maksimum(3.14, 2.71) membuat instance terpisah double maksimum(double, double). Setiap instance adalah fungsi konkret yang berdiri sendiri.

Kode: Template maksimum #

cpp
#include <iostream>
#include <string>

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

int main() {
    std::cout << "maks int    = " << maksimum(3, 7) << '\n';
    std::cout << "maks double = " << maksimum(3.14, 2.71) << '\n';
    std::cout << "maks string = " << maksimum(std::string("kucing"),
                                              std::string("anjing")) << '\n';
    std::cout << "maks long   = " << maksimum<long>(10, 20) << '\n';
    return 0;
}

Output yang Dihasilkan:

text
maks int    = 7
maks double = 3.14
maks string = kucing
maks long   = 20
  1. Baris template <typename T>: mendeklarasikan parameter tipe T. typename bisa diganti dengan class — secara semantik identik untuk template.
  2. Baris T maksimum(T a, T b): tipe parameter dan return type sama-sama T. Saat dipanggil dengan int, seluruh T diganti int.
  3. Baris maksimum(3, 7): compiler menyimpulkan T = int dari argumen. Tidak perlu menyebut tipe eksplisit.
  4. Baris maksimum<long>(10, 20): argumen eksplisit <long> memaksa T = long. Berguna bila inference tidak unik, mis. maksimum(3, 3.5).

Class Template #

Class template menggunakan parameter tipe untuk anggota dan method. STL penuh dengan class template: std::vector<T>, std::map<K,V>, std::stack<T>.

Mekanisme: Instantiation Saat Deklarasi #

Saat kita menulis Stack<int> si;, compiler membuat versi konkret Stack dengan T = int — termasuk semua method yang dipakai. Method yang tidak dipanggil tidak dihasilkan, menghemat ukuran binary.

Kode: Stack Generik #

cpp
#include <iostream>
#include <vector>
#include <string>
#include <stdexcept>

template <typename T>
class Stack {
private:
    std::vector<T> data_;
public:
    void push(const T& v) { data_.push_back(v); }
    T pop() {
        if (data_.empty()) throw std::runtime_error("stack kosong");
        T atas = data_.back();
        data_.pop_back();
        return atas;
    }
    bool empty() const { return data_.empty(); }
};

int main() {
    Stack<int> si;
    si.push(1);
    si.push(2);
    std::cout << "int pop = " << si.pop() << '\n';

    Stack<std::string> ss;
    ss.push("halo");
    ss.push("dunia");
    std::cout << "str pop = " << ss.pop() << '\n';
    return 0;
}

Output yang Dihasilkan:

text
int pop = 2
str pop = dunia
  1. Baris template <typename T> class Stack: parameter tipe T tersedia untuk seluruh anggota kelas.
  2. Baris std::vector<T> data_: anggota data_ bertipe vector<T> — tipe yang bergantung pada parameter template.
  3. Baris void push(const T& v): parameter const T& agar tidak terjadi copy mahal untuk tipe besar seperti std::string.
  4. Baris Stack<int> si;: instantiasi Stack dengan T = int. Compiler menghasilkan class konkret Stack_int.
  5. Baris Stack<std::string> ss;: instantiasi terpisah dengan T = std::string. Method push dan pop dihasilkan untuk tipe ini.

Multi-Parameter dan Trailing Return Type #

Template menerima lebih dari satu parameter tipe. Kita juga bisa memakai auto + decltype untuk return type yang bergantung pada parameter.

Kode: Penjumlahan Multi-Tipe #

cpp
#include <iostream>

template <typename T, typename U>
auto tambah(T a, U b) -> decltype(a + b) {
    return a + b;
}

int main() {
    std::cout << "int + double  = " << tambah(3, 4.5) << '\n';
    std::cout << "double + int  = " << tambah(2.5, 4) << '\n';
    return 0;
}

Output yang Dihasilkan:

text
int + double  = 7.5
double + int  = 6.5
  1. Baris template <typename T, typename U>: dua parameter tipe yang berbeda — T dan U bisa berbeda tipe.
  2. Baris auto tambah(T a, U b) -> decltype(a + b): trailing return type. decltype(a + b) menyimpulkan tipe hasil dari ekspresi a + b. Untuk int + double, hasil adalah double (promotion).
  3. Baris tambah(3, 4.5): T = int, U = double. Hasil 3 + 4.5 = 7.5 bertipe double.
  4. Baris tambah(2.5, 4): T = double, U = int. Hasil 2.5 + 4 = 6.5.

Type Deduction dan static_assert #

Terkadang kita ingin membatasi tipe yang valid untuk template. C++ menyediakan <type_traits> untuk memeriksa sifat tipe pada saat kompilasi.

Kode: Membatasi Template ke Integer #

cpp
#include <iostream>
#include <type_traits>

template <typename T>
T factorial(T n) {
    static_assert(std::is_integral<T>::value,
                  "factorial hanya untuk tipe integer");
    T hasil = 1;
    for (T i = 2; i <= n; ++i) hasil *= i;
    return hasil;
}

int main() {
    std::cout << "5!  = " << factorial(5) << '\n';
    std::cout << "10! = " << factorial(10) << '\n';
    // factorial(3.5); // ERROR: static_assert gagal
    return 0;
}

Output yang Dihasilkan:

text
5!  = 120
10! = 3628800
  1. Baris static_assert(std::is_integral<T>::value, "..."): pemeriksaan saat kompilasi. Bila T bukan tipe integer (mis. double), kompilasi gagal dengan pesan error yang jelas.
  2. Baris factorial(5): T = int (disimpulkan dari literal). 5! = 120.
  3. Baris factorial(10): masih dalam rentang int, 10! = 3628800. Untuk n lebih besar, gunakan long long.

Ringkasan #

  • Function template menghasilkan fungsi konkret saat dipanggil; tipe dapat disimpulkan otomatis atau dispesifikasikan eksplisit.
  • Class template memungkinkan struktur data generik — pondasi seluruh STL.
  • Template memerlukan operasi yang tersedia pada tipe; gunakan static_assert + <type_traits> untuk membatasi.
  • Multi-parameter template dengan auto + decltype menghasilkan tipe return yang fleksibel.
  • Template harus didefinisikan di header agar terlihat saat instantiation.