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 #
#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:
maks int = 7
maks double = 3.14
maks string = kucing
maks long = 20- Baris
template <typename T>: mendeklarasikan parameter tipeT.typenamebisa diganti denganclass— secara semantik identik untuk template. - Baris
T maksimum(T a, T b): tipe parameter dan return type sama-samaT. Saat dipanggil denganint, seluruhTdigantiint. - Baris
maksimum(3, 7): compiler menyimpulkanT = intdari argumen. Tidak perlu menyebut tipe eksplisit. - Baris
maksimum<long>(10, 20): argumen eksplisit<long>memaksaT = 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 #
#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:
int pop = 2
str pop = dunia- Baris
template <typename T> class Stack: parameter tipeTtersedia untuk seluruh anggota kelas. - Baris
std::vector<T> data_: anggotadata_bertipevector<T>— tipe yang bergantung pada parameter template. - Baris
void push(const T& v): parameterconst T&agar tidak terjadi copy mahal untuk tipe besar sepertistd::string. - Baris
Stack<int> si;: instantiasiStackdenganT = int. Compiler menghasilkan class konkretStack_int. - Baris
Stack<std::string> ss;: instantiasi terpisah denganT = std::string. Methodpushdanpopdihasilkan 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 #
#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:
int + double = 7.5
double + int = 6.5- Baris
template <typename T, typename U>: dua parameter tipe yang berbeda —TdanUbisa berbeda tipe. - Baris
auto tambah(T a, U b) -> decltype(a + b): trailing return type.decltype(a + b)menyimpulkan tipe hasil dari ekspresia + b. Untukint + double, hasil adalahdouble(promotion). - Baris
tambah(3, 4.5):T = int,U = double. Hasil3 + 4.5 = 7.5bertipedouble. - Baris
tambah(2.5, 4):T = double,U = int. Hasil2.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 #
#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:
5! = 120
10! = 3628800- Baris
static_assert(std::is_integral<T>::value, "..."): pemeriksaan saat kompilasi. BilaTbukan tipe integer (mis.double), kompilasi gagal dengan pesan error yang jelas. - Baris
factorial(5):T = int(disimpulkan dari literal).5! = 120. - Baris
factorial(10): masih dalam rentangint,10! = 3628800. Untuknlebih besar, gunakanlong 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+decltypemenghasilkan tipe return yang fleksibel. - Template harus didefinisikan di header agar terlihat saat instantiation.