Episode ini membahas template sebagai jantung generic programming: function templates dan class templates, template specialization dan variadic templates, concepts dan type traits, serta std::optional dan std::variant untuk nilai opsional dan bervariasi tipe.

Salah satu kekuatan terbesar C++ adalah template: cara menulis kode yang bekerja untuk berbagai tipe data tanpa menduplikasi kode dan tanpa mengorbankan kinerja. Inilah yang membedakan generic programming C++ dari generics di bahasa lain — template dipakai sebagai cetak biru, dan kode nyata dibangkitkan saat kompilasi.
Episode 10 membedah template dari fungsi dan class sederhana, hingga specialization, variadic templates, dan konsep modern seperti concepts dan type traits. Kalian juga akan berkenalan dengan std::optional dan std::variant yang membuat API kalian lebih ekspresif.
Function template memungkinkan satu fungsi bekerja untuk tipe apa pun. Tipe ditentukan saat kompilasi dari argumen yang diberikan:
cat > fntmpl.cpp <<'EOF'
#include <iostream>
template <typename T>
T maksimum(T a, T b) {
return (a > b) ? a : b;
}
int main() {
std::cout << maksimum(3, 7) << "\n";
std::cout << maksimum(2.5, 1.8) << "\n";
}
EOF
g++ -std=c++20 fntmpl.cpp -o fntmpl
./fntmpltemplate <typename T> T maksimum(T a, T b) mendefinisikan fungsi generik. Saat dipanggil dengan int, compiler membangkitkan versi int; saat dipanggil dengan double, versi double — tanpa overhead runtime.
Compiler menebak T dari argumen, jadi maksimum(3, 7) menyimpulkan T = int. Jika kedua argumen bertipe berbeda, kalian bisa memaksa dengan maksimum<double>(3, 7). Deduksi bekerja baik selama kedua argumen bertipe sama; jika tidak, butuh dua parameter template atau konversi eksplisit.
Class template adalah class yang bertipe parameter. std::vector<int> sebenarnya adalah class template vector<T> dengan T = int. Membuat class template sendiri berguna untuk struktur data yang harus bekerja untuk banyak tipe:
cat > classtmpl.cpp <<'EOF'
#include <iostream>
#include <string>
template <typename T>
class Kotak {
public:
Kotak(T nilai) : nilai_(nilai) {}
T nilai() const { return nilai_; }
private:
T nilai_;
};
int main() {
Kotak<int> k1(42);
Kotak<std::string> k2("halo");
std::cout << k1.nilai() << " " << k2.nilai() << "\n";
}
EOF
g++ -std=c++20 classtmpl.cpp -o classtmpl
./classtmplKotak<int> k1(42) menginstansiasi class template dengan T = int, dan Kotak<std::string> k2("halo") dengan T = std::string. Dua instansiasi itu adalah dua class berbeda yang masing-masing dibangkitkan saat kompilasi.
Template specialization menangani kasus khusus untuk tipe tertentu — kalian bisa menspesialisasikan seluruh template atau hanya sebagian. Contoh: template <> std::string deskripsi<bool>(bool) adalah full specialization untuk bool, dipakai hanya saat T = bool, sementara tipe lain memakai versi generik. Ini berguna ketika perilaku umum tidak cocok untuk tipe tertentu.
Variadic template menerima jumlah argumen yang tidak tentu dan menjadi dasar fungsi seperti std::printf yang aman tipe. Pola template <typename T, typename... Rest> memisahkan argumen pertama dari sisanya, lalu memanggil dirinya lagi dengan sisa... sampai tidak ada argumen tersisa. Karena kode dibangkitkan untuk setiap kombinasi argumen, pemrosesannya berjalan rekursif.
Sejak C++20, concepts memberi nama pada persyaratan tipe. Dengan concepts, kesalahan template terdeteksi dengan pesan yang jelas, bukan halaman error yang membingungkan:
cat > concept.cpp <<'EOF'
#include <iostream>
#include <concepts>
template <std::integral T>
T ganda(T nilai) {
return nilai * 2;
}
int main() {
std::cout << ganda(21) << "\n";
}
EOF
g++ -std=c++20 concept.cpp -o concept
./concepttemplate <std::integral T> membatasi T pada tipe integral seperti int atau long. Memanggil ganda(3.5) menghasilkan error kompilasi yang jelas karena double bukan integral.
Type traits dalam <type_traits> memungkinkan kalian memeriksa dan memodifikasi properti tipe saat kompilasi — std::is_integral_v<T>, std::remove_reference_t<T>, dan sejenisnya memberi introspeksi tipe. Dengan static_assert(std::is_integral_v<int>{:bash} ...), kalian memvalidasi asumsi saat kompilasi; jika gagal, program tidak akan terbuild.
std::optional<T> membungkus nilai T yang mungkin tidak ada dan menggantikan kode klasik yang memakai nilai sentinel seperti -1 atau pointer null. std::nullopt menandakan tidak ada nilai, dan *hasil mengambil nilai yang ada. Tipe fungsi sekarang mengungkapkan kemungkinan kegagalan secara eksplisit.
std::variant<T1, T2, ...> menyimpan satu nilai dari beberapa tipe yang mungkin. Hanya satu alternatif yang aktif pada satu waktu, dan akses diperiksa aman:
cat > variant.cpp <<'EOF'
#include <iostream>
#include <variant>
int main() {
std::variant<int, std::string> v;
v = 42;
std::cout << std::get<int>(v) << "\n";
v = "teks";
if (auto* p = std::get_if<int>(&v)) {
std::cout << "int: " << *p << "\n";
} else {
std::cout << "bukan int\n";
}
}
EOF
g++ -std=c++20 variant.cpp -o variant
./variantstd::get_if<int>(&v) mengembalikan pointer ke nilai jika v sedang menyimpan int, atau nullptr jika tidak. variant dan optional membuat kode kalian menyatakan kemungkinan lebih jujur daripada tipe polos.
Inti yang harus dibawa pulang:
Kotak<int> adalah dasar generic programming.std::optional dan std::variant menyatakan nilai opsional dan multi-tipe dengan aman.Di episode 11 selanjutnya kita akan membahas error handling dan debugging — exceptions dengan throw dan catch, stack unwinding, jenis exception standar dan custom, debugging dengan GDB dan sanitizers, serta static analysis memakai clang-tidy dan cppcheck.