Menu

Function Overloading C++: Nama Sama, Parameter Berbeda

Function overloading C++ membuat beberapa fungsi bisa memakai satu nama asalkan daftar parameternya berbeda. Pelajari cara overload resolution memilih yang cocok, kenapa tipe kembalian saja tidak dihitung, serta jebakan ambiguitas dan argumen default yang perlu dihindari.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Satu Nama, Banyak Versi

Kamu sering butuh operasi yang sama untuk jenis data yang berbeda: mencetak int, mencetak string, mencetak double. Di beberapa bahasa kamu harus membuat printInt, printString, printDouble. C++ membiarkanmu memberi semuanya nama yang sama dan membedakannya berdasarkan parameternya. Itulah function overloading.

Aturannya sederhana: beberapa fungsi boleh memakai satu nama asalkan daftar parameternya berbeda, baik dalam jumlah parameter, tipe, maupun urutannya. Compiler melihat argumen di setiap tempat pemanggilan dan memilihkan versi yang cocok untukmu.

Tiga fungsi, satu nama. Setiap pemanggilan jatuh ke versi yang tipe parameternya sesuai dengan argumennya. Inilah yang membuat std::cout << x berfungsi untuk int, double, maupun string: operator<< di-overload berkali-kali.

Apa yang Dihitung sebagai Overload Berbeda

Sebuah overload dibedakan hanya dari daftar parameternya. Kamu bisa memvariasikan:

int  area(int side);                 // 1 param
int  area(int width, int height);    // 2 params  -> different
double area(double r);               // different type -> different

void log(string msg, int level);     // order matters...
void log(int level, string msg);     // ...so this is different too

Masing-masing adalah overload yang sah dan berbeda. Compiler menyusun kumpulan kandidat dari semua fungsi bernama area, lalu mencocokkannya berdasarkan jumlah dan tipe argumen.

Tipe Kembalian Saja Tidak Cukup

Inilah jebakan yang menjebak hampir semua orang: kamu tidak bisa melakukan overload berdasarkan tipe kembalian. Nilai kembalian tidak berperan dalam memilih overload, karena compiler menentukan fungsi mana yang dipanggil dari argumen-nya, sebelum ia melihat apa yang dikembalikan.

int    convert(double x);   // OK
double convert(double x);   // ERROR: redefinition - only the return type differs

Kode itu tidak akan bisa dikompilasi. Kalau daftar parameternya identik, kedua deklarasi itu dianggap fungsi yang sama dalam urusan overloading, dan kamu mendapat error redefinition. Untuk membedakan berdasarkan tipe hasil, ubah salah satu parameter (atau pakai template / type cast di tempat pemanggilan).

Cara Overload Resolution Memilih Pemenang

Saat kamu melakukan pemanggilan, compiler memeringkat setiap overload yang memungkinkan dan memilih kecocokan terbaik. Secara garis besar, urutan yang diutamakannya adalah:

  1. Kecocokan persis (tanpa konversi).
  2. Promosi (misalnya char atau short -> int, float -> double).
  3. Konversi standar (misalnya int -> double, double -> int, pointer ke base).

Kalau tepat satu overload lebih baik daripada semua yang lain, dialah pemenangnya. Perhatikan bagaimana kecocokan persis mengalahkan konversi:

'A' adalah char, tetapi promosi ke int peringkatnya lebih tinggi daripada konversi ke double, jadi yang dipanggil adalah overload int. Aturan peringkat inilah yang membuat overload resolution biasanya "langsung benar", dan sesekali mengejutkanmu.

Jebakan Ambiguitas

Kalau dua overload sama bagusnya, tidak ada yang lebih baik, compiler menolak menebak dan melaporkan ambiguous call. Contoh buku teksnya adalah dua overload yang masing-masing butuh konversi dengan peringkat yang sama:

void f(int x);
void f(double x);

f(0L);   // ERROR: ambiguous - long -> int and long -> double are equal-rank conversions

Baik int maupun double bukan kecocokan persis untuk long, dan kedua konversinya berada di peringkat yang sama, jadi pemanggilannya ambigu. Ada dua perbaikan yang rapi:

Kejutan yang serupa: meneruskan string literal. void g(const string&) dan void g(bool) sama-sama bersaing untuk g("hi"), dan bool bisa menang, karena const char* dikonversi ke bool (bukan null -> true) dengan langkah yang lebih sedikit daripada membangun std::string. Kalau kamu melihat string literal secara misterius memanggil overload bool-mu, inilah alasannya. Tambahkan overload const char* atau const string& supaya ada yang cocok persis.

Overloading dan Argumen Default Tidak Cocok Dicampur

Argumen default bukan pengganti overloading, dan menggabungkan keduanya menimbulkan ambiguitas. Keduanya bisa menjawab pemanggilan yang sama, sehingga compiler tidak bisa memilih:

void connect(string host, int port = 8080);   // can be called with 1 arg
void connect(string host);                     // also callable with 1 arg

connect("localhost");   // ERROR: ambiguous - both match a single argument

Pilih satu cara untuk setiap bentuk pemanggilan. Pakai argumen default saat perilakunya identik dan kamu hanya ingin parameter yang opsional; pakai overloading saat daftar argumen yang berbeda memang harus menjalankan kode yang benar-benar berbeda. Mencampur keduanya sehingga dua signature bertabrakan untuk jumlah argumen yang sama pasti menghasilkan error ambiguitas.

Satu perbedaan lagi yang perlu dipahami betul: overloading bukan overriding. Overloading ditentukan saat kompilasi, di antara fungsi-fungsi dalam scope yang sama dengan nama yang sama tetapi parameter berbeda. Overriding menggantikan fungsi virtual di class turunan saat program berjalan dan membutuhkan signature yang sama, topik yang dibahas di virtual function nanti.

Berikutnya: Lambda

Overloading memberi satu nama beberapa implementasi bertipe yang dipilih saat kompilasi. Namun kadang kamu sama sekali tidak ingin fungsi bernama: kamu butuh fungsi kecil sekali pakai yang didefinisikan tepat di tempat kamu memakainya, sering kali untuk diteruskan ke algoritma seperti sort. Persis itulah lambda: fungsi anonim yang bisa kamu tulis inline, bisa menangkap variabel di sekitarnya, dan bisa kamu serahkan dalam satu ekspresi. Berikutnya kita akan melihat cara menulisnya dan kapan lambda lebih unggul daripada fungsi bernama yang lengkap.

Pertanyaan yang sering diajukan

Apa itu function overloading di C++?

Function overloading membuatmu bisa mendefinisikan beberapa fungsi dengan nama yang sama asalkan daftar parameternya berbeda (dalam jumlah, tipe, atau urutannya). Compiler memilih mana yang dipanggil berdasarkan argumen yang kamu berikan, jadi print(42) dan print("hi") bisa memanggil dua fungsi print yang berbeda.

Bisakah dua fungsi C++ hanya berbeda di tipe kembaliannya?

Tidak. Overload harus berbeda di daftar parameternya. int f(int) dan double f(int) adalah compile error, karena tipe kembalian bukan bagian dari signature yang dipakai untuk overload resolution. Compiler memilih overload dari argumen di tempat pemanggilan, sebelum nilai kembaliannya dipakai.

Apa penyebab error "ambiguous call" pada fungsi yang di-overload?

Error ini terjadi saat dua overload sama-sama cocok dan compiler tidak bisa memilih salah satunya. Contoh klasiknya adalah f(int) dan f(double) yang dipanggil dengan f(0L) (sebuah long), ketika keduanya membutuhkan konversi dengan peringkat yang sama. Perbaiki dengan menambahkan overload yang cocok persis atau melakukan cast pada argumen ke tipe yang kamu inginkan.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai