Topik ini membahas function template, yaitu definisi fungsi yang parameternya meliputi tipe data sehingga satu definisi dapat melayani banyak tipe. Pembahasan dimulai dari sintaks template<typename T>, dilanjutkan proses instantiation yang menghasilkan fungsi konkret saat kompilasi, dan berakhir pada dasar-dasar type deduction yang menentukan tipe T dari argumen pemanggilan. Seluruh contoh berupa program utuh yang dapat dikompilasi dan dijalankan, lengkap dengan keluaran aslinya.
Deklarasi function template#
Sebuah function template dideklarasikan dengan menulis daftar parameter template di depan fungsi biasa. Pada template<typename T>, nama T berperan sebagai parameter tipe, yaitu tempat yang akan diisi sebuah tipe konkret ketika template dipanggil. Nama tersebut bebas diganti, misalnya U atau Elemen, karena hanya sebuah penanda. Badan fungsinya ditulis seolah-olah T sudah menjadi tipe konkret, sehingga operator dan method yang dipakai di dalamnya harus tersedia pada setiap tipe yang digunakan nantinya.
Program berikut mendefinisikan template maksimum yang membandingkan dua nilai, lalu memanggilnya dengan tiga tipe yang berbeda. Simpan berkasnya, kompilasi, dan jalankan untuk melihat keluarannya.
#include <iostream>
#include <string>
template <typename T>
T maksimum(T a, T b) {
if (a > b) {
return a;
}
return b;
}
int main() {
std::cout << maksimum(10, 24) << "\n";
std::cout << maksimum(3.5, 2.75) << "\n";
std::cout << maksimum(std::string("pagi"), std::string("sore")) << "\n";
std::cout << maksimum<double>(7, 4.5) << "\n";
return 0;
}24
3.5
sore
7Pemanggilan pertama menurunkan T menjadi int, pemanggilan kedua menjadi double, dan pemanggilan ketiga menjadi std::string. Pemanggilan keempat menuliskan argumen template secara eksplisit dengan maksimum<double>(7, 4.5), sehingga T tidak diturunkan dan 7 yang bertipe int dikonversi menjadi double sebelum perbandingan. Dari satu definisi tersebut, compiler menyiapkan fungsi yang tepat untuk setiap pemanggilan tanpa penulisan ulang apa pun.
Instantiation saat kompilasi#
Instantiation adalah proses compiler mengganti parameter tipe dengan tipe konkret, lalu menghasilkan fungsi yang benar-benar dipanggil. Proses ini berlangsung saat kompilasi, bukan saat program berjalan. Setiap kombinasi tipe yang dipanggil menghasilkan satu instans tersendiri, sehingga maksimum<int> dan maksimum<double> adalah dua fungsi yang berbeda meskipun definisinya hanya satu.
Fungsi hanya dibuat untuk tipe yang benar-benar dipakai. Apabila maksimum tidak pernah dipanggil dengan std::string, compiler tidak menghasilkan instans untuk std::string sama sekali. Sifat ini menjaga ukuran program tetap sebanding dengan pemakaian nyatanya, tetapi menuntut definisi template terlihat oleh compiler di tempat pemanggilan, karena penggantian T tidak mungkin dilakukan tanpa badan fungsinya. Praktik yang mengikuti aturan itu adalah menaruh definisi template pada header.
Sisi lain dari mekanisme ini perlu diperhitungkan pada proyek besar. Template yang dipanggil dengan banyak tipe menghasilkan banyak instans, dan setiap instans menjadi kode mesin tersendiri yang harus dikompilasi dan disimpan. Instans yang muncul di banyak berkas digabungkan oleh linker menjadi satu salinan, sehingga pengulangan tidak berlipat ganda, tetapi waktu kompilasi tetap bertambah seiring banyaknya kombinasi tipe yang dipakai.
Type deduction dan konflik deduksinya#
Type deduction adalah mekanisme compiler menentukan argumen template dari argumen fungsi. Compiler mencocokkan tipe setiap argumen dengan parameter yang memuat T, lalu menuntut hasil deduksinya konsisten antarargumen. Apabila satu pemanggilan menurunkan T ke dua tipe yang berbeda, kompilasi gagal. Deduksi berlaku sama untuk function template maupun untuk method pada class template, sehingga mekanisme ini menjadi dasar bagi seluruh fitur pada modul ini.
// tidak-dijalankan (contoh error kompilasi)
std::cout << maksimum(3, 4.5);Pemanggilan tersebut ditolak dengan pesan no matching function for call to 'maksimum(int, double)', karena 3 menurunkan T menjadi int sedangkan 4.5 menurunkannya menjadi double. Ada dua perbaikan yang lazim, yaitu menyebut argumen template secara eksplisit dengan maksimum<double>(3, 4.5) atau menyamakan tipe kedua argumen terlebih dahulu. Pada template dengan beberapa parameter tipe, setiap parameter menampung satu tipe tersendiri, sehingga konflik semacam ini tidak muncul.
Syarat yang harus dipenuhi tipe T#
Deklarasi template hampir tidak menuntut apa pun dari T, tetapi badan fungsinya menuntut operasi tertentu. Template maksimum membandingkan a > b, sehingga setiap tipe yang dipakai wajib menyediakan operator >. Syarat ini diperiksa saat instantiation, yaitu ketika T sudah menjadi tipe konkret. Urutan pemeriksaan ini menjelaskan bentuk pesan errornya. Compiler menolak baris di dalam badan template terlebih dahulu, lalu menunjuk pemanggilan yang memicunya.
// tidak-dijalankan (contoh error kompilasi)
struct Nilai {
int angka;
};
std::cout << maksimum(Nilai{3}, Nilai{5}).angka;Compiler menolak pemanggilan tersebut dengan pesan no match for 'operator>' (operand types are 'Nilai' and 'Nilai'). Pesannya muncul karena instantiation maksimum<Nilai> membutuhkan operator > yang tidak dimiliki Nilai. Perbaikannya menyediakan operator> untuk Nilai atau menuliskan template yang tidak bergantung pada operator perbandingan. Kebutuhan semacam ini masih dinyatakan secara tersirat pada C++17, dan cara menyatakannya secara eksplisit dibahas pada topik concept.
Kesalahan umum#
Tiga kekeliruan berikut paling sering muncul ketika function template baru mulai dipakai.
- Menempatkan definisi template pada berkas
.cppyang terpisah dari pemanggilnya. Compiler yang memproses berkas pemanggil tidak melihat badan template, sehingga instantiation tidak terjadi dan linker melaporkanundefined reference. Definisi template ditempatkan pada header agar terlihat oleh setiap berkas yang memanggilnya. - Mengira
maksimum(3, 4.5)akan menghasilkandoublesecara otomatis. Deduksi menuntut kedua argumen menurunkanTyang sama, sehingga pemanggilan campuran ditolak compiler. Perbaikannya menyebut tipe secara eksplisit atau menyamakan tipe argumennya. - Mengharapkan pemanggilan dengan tipe yang tidak mendukung operasi badan template tetap lolos. Error baru muncul saat instantiation, bukan pada baris deklarasi template, sehingga pesannya menunjuk baris di dalam badan template. Pastikan setiap tipe yang dipakai menyediakan operator dan method yang diperlukan.
Ringkasan#
- Function template mendefinisikan satu fungsi untuk banyak tipe melalui parameter tipe, dan setiap kombinasi tipe yang dipanggil menghasilkan satu instans tersendiri saat kompilasi.
- Type deduction menentukan
Tdari argumen pemanggilan, sedangkan dua tipe berbeda dalam satu pemanggilan menggagalkan deduksi kecuali argumen template ditentukan secara eksplisit. - Badan template diperiksa saat instantiation, sehingga setiap tipe yang dipakai wajib menyediakan operator dan method yang diperlukan badan template.
Referensi#
- Function template — cppreference — Aturan deklarasi function template dan instantiation-nya.
- Template argument deduction — cppreference — Aturan penurunan argumen template dari argumen fungsi.
- Function templates — LearnCpp — Tutorial bertahap tentang function template dan deduksinya.