Tulis sekali, pakai untuk semua tipe
Di halaman sebelumnya kamu mengurutkan vector<int> dengan std::sort. Tapi std::sort juga bisa mengurutkan vector<string>, vector<double>, atau array berisi struct buatanmu sendiri, tanpa ada yang menulis sort terpisah untuk masing-masing. Itu bukan sihir dan bukan overloading. Itu adalah template: satu potong kode yang dipakai ulang oleh compiler untuk tipe apa pun yang kamu berikan.
Tanpa template, kamu terpaksa menyalin logika yang sama untuk setiap tipe. Berikut fungsi maximum yang sama ditulis tiga kali, persis duplikasi yang ingin dihilangkan oleh template:
int maximum(int a, int b) { return a > b ? a : b; }
double maximum(double a, double b) { return a > b ? a : b; }
string maximum(string a, string b) { return a > b ? a : b; }
Isi fungsinya identik. Hanya tipenya yang berbeda. Template memungkinkan kamu mengatakan "ini bekerja untuk tipe T apa pun" dan menulisnya sekali saja.
Function template
Kamu mengubah sebuah fungsi menjadi template dengan menambahkan template <typename T> di depannya dan memakai T di setiap tempat yang biasanya diisi tipe konkret.
Perhatikan bahwa kamu tidak pernah menulis maximum<int> atau maximum<double>. Compiler melihat argumennya dan mencari tahu T seharusnya apa. Itulah template argument deduction. Setiap tipe berbeda yang kamu pakai untuk memanggilnya membuat compiler menginstansiasi (membuat) fungsi konkret yang terpisah di balik layar.
Kamu bisa menuliskan tipenya secara eksplisit saat deduksi tidak bisa membantu, dengan kurung siku:
Ada jebakan umum yang bersembunyi di deduksi. Karena T harus berupa satu tipe, mencampur tipe argumen akan merusaknya:
maximum(3, 7.5); // ERROR: is T int or double? The compiler refuses to guess.
Kamu bisa memperbaikinya dengan menulis secara eksplisit, maximum<double>(3, 7.5), atau dengan memberi setiap parameter parameter tipenya sendiri, yang akan kita lakukan berikutnya.
Beberapa parameter tipe
Template tidak terbatas pada satu tipe. Tulis sebanyak yang kamu butuhkan, dipisahkan koma. Inilah cara menulis fungsi yang parameternya bisa bertipe berbeda:
Kalau tipe kembaliannya bergantung pada parameternya, biarkan compiler yang menentukannya dengan auto (C++14 ke atas), yang cocok dipadukan dengan template:
Class template
Template bukan hanya untuk fungsi: seluruh class juga bisa dibuat template. Beginilah cara container standar bekerja: vector<int>, map key-value, dan pair<A, B> semuanya adalah class template. Kamu menulis struktur datanya sekali dan ia menyimpan tipe apa pun yang kamu jadikan parameternya.
Berikut Box generik sederhana yang menyimpan satu nilai bertipe apa saja:
Perbedaan utamanya dengan function template: pada class template kamu biasanya harus menuliskan tipenya di dalam kurung siku, Box<int>, karena di standar yang lebih lama tidak ada argumen constructor yang bisa dipakai untuk deduksi. (C++17 menambahkan class template argument deduction, jadi Box b(42); juga bisa, tapi menulis secara eksplisit selalu aman dan jelas dibaca.)
Error-nya akan sangat panjang, dan ini sebabnya
Bagian inilah yang menjebak semua orang, jadi perlu dikatakan dengan terus terang. Template baru dicek sepenuhnya saat diinstansiasi dengan tipe sungguhan. Kamu bisa menulis template yang memakai <, dan template itu bisa dikompilasi dengan baik sendirian. Error-nya baru muncul saat kamu menginstansiasinya dengan tipe yang tidak punya <.
template <typename T>
T maximum(T a, T b) {
return a > b ? a : b; // requires that T supports >
}
struct Point { int x, y; };
// maximum(Point{1,2}, Point{3,4});
// ERROR: no operator > for Point. The message names Point AND
// quotes this whole function, often spanning many lines.
Karena compiler memasukkan tipe lengkapnya ke dalam template dan melaporkan kegagalan dari dalam kode yang dihasilkan, satu kesalahan bisa menghasilkan tumpukan output yang menyebut bagian dalam library. Dua tips bertahan hidup:
- Baca error pertama, bukan yang terakhir. Error-error berikutnya biasanya hanya akibat dari yang pertama.
- Cari nama tipe milikmu sendiri di dalam pesannya (di sini,
Point). Itu memberi tahu instansiasi mana yang salah.
Solusi sebenarnya adalah memastikan tipemu mendukung apa pun yang dibutuhkan template. Untuk maximum, artinya overloading operator> untuk Point, topik untuk halaman nanti. Concept di C++20 modern bisa memunculkan error ini lebih awal dan membuatnya mudah dibaca, tapi model substitusi di baliknya tetap sama.
Berikutnya: class
Kamu baru saja membangun class template Box, yaitu class dengan data private, constructor, dan member function, sambil berfokus pada template-nya. Halaman berikutnya berjalan lebih pelan dan mengajarkan class dengan benar: cara menggabungkan data dengan fungsi-fungsi yang bekerja padanya, apa yang sebenarnya dikendalikan public dan private, dan bagaimana member function mengakses status milik objek itu sendiri. Template dan class terus-menerus dipadukan di C++ sungguhan, jadi pemahaman yang kuat tentang class membuat kode generik jauh lebih mudah ditulis.
Pertanyaan yang sering diajukan
Apa itu template di C++?
Template adalah cetak biru yang memungkinkan kamu menulis fungsi atau class sekali, lalu compiler membuatkan versinya untuk setiap tipe yang kamu pakai. Kamu menulis template <typename T> lalu memakai T sebagai pengganti tipe yang sebenarnya. Compiler mencetak versi konkretnya, dan proses ini disebut instansiasi.
Apa perbedaan typename dan class di template C++?
Di daftar parameter template, template <typename T> dan template <class T> artinya persis sama. Sekarang typename umumnya lebih disukai karena lebih jujur dibaca: T bisa tipe apa saja, bukan hanya class. Pilihan kata kuncinya tidak berpengaruh pada kode yang dihasilkan.
Kenapa pesan error template C++ begitu panjang?
Template dicek saat diinstansiasi dengan tipe sungguhan, bukan saat ditulis. Kalau sebuah tipe tidak mendukung operasi yang kamu pakai (seperti < untuk sorting), error-nya muncul jauh di dalam kode library dengan tipe hasil instansiasi yang ditulis lengkap, sehingga output-nya bisa berlembar-lembar. Baca error pertama dan cari nama tipe milikmu di dalamnya.