Di dunia nyata, satu perintah bisa punya banyak bentuk: “cetak” untuk foto, dokumen, atau struk — perintahnya sama, isinya berbeda. C++ memberi fungsi kemampuan yang sama lewat function overloading: beberapa fungsi boleh memakai nama yang sama, asalkan daftar parameternya berbeda — jumlahnya, tipenya, atau urutan tipenya. Compiler yang memilih versi mana yang sesuai pada setiap pemanggilan, melalui mekanisme bernama overload resolution. Pada Chapter ini kita menulis beberapa overload, mengintip cara compiler menimbang, dan mengukir dua aturan yang sering menjebak: return type tidak ikut membedakan, serta benturan halus dengan default arguments.

Nama Sama, Parameter Berbeda #

Overload dinyatakan seperti fungsi biasa, hanya nama-namanya sama. Berikut tiga overload tampilkan yang berbeda tipe parameter, ditambah dua overload luas yang berbeda jumlah parameter:

cpp
#include <iostream>
#include <string>

// Tiga overload: nama sama, daftar parameter berbeda
void tampilkan(int nilai) {
    std::cout << "int    : " << nilai << "\n";
}

void tampilkan(double nilai) {
    std::cout << "double : " << nilai << "\n";
}

void tampilkan(std::string teks) {
    std::cout << "string : " << teks << "\n";
}

// Overload berdasarkan JUMLAH parameter
double luas(double sisi) {
    return sisi * sisi;
}

double luas(double panjang, double lebar) {
    return panjang * lebar;
}

int main() {
    tampilkan(42);              // exact match: int
    tampilkan(2.5);             // exact match: double
    tampilkan("halo");          // const char* dikonversi ke std::string
    tampilkan('A');             // char dipromosikan ke int

    std::cout << "Luas persegi 4x4 : " << luas(4.0) << "\n";
    std::cout << "Luas 4.0 x 2.5   : " << luas(4.0, 2.5) << "\n";
    return 0;
}

Output yang Dihasilkan:

text
int    : 42
double : 2.5
string : halo
int    : 65
Luas persegi 4x4 : 16
Luas 4.0 x 2.5   : 10

Empat pemanggilan tampilkan menunjukkan empat jalan pemilihan yang berbeda. tampilkan(42) dan tampilkan(2.5) masing-masing menemukan parameter dengan tipe yang persis sama (exact match). tampilkan("halo") tidak punya overload const char*, tetapi teks literal bisa dikonversi ke std::string, sehingga overload ketiga terpilih. Yang paling menarik adalah tampilkan('A'): tidak ada overload char, jadi compiler mempromosikan char ke int (nilai kodenya, 65) dan memilih overload pertama — output int : 65 adalah bukti pemilihan itu. Dua pemanggilan luas membuktikan bahwa JUMLAH parameter juga menjadi pembeda sah: satu argumen memilih luas persegi, dua argumen memilih luas persegi panjang.

Cara Compiler Memilih: Resolusi Overload #

Saat menemui pemanggilan yang mengandung banyak kandidat, compiler menilai setiap overload dengan urutan prioritas yang bisa kita sederhanakan menjadi tiga lapis. Pertama, exact match: tipe argumen sama persis dengan parameter — pemenang pasti. Kedua, promotion — konversi yang tidak menghilangkan nilai, seperti char/short ke int atau float ke double. Ketiga, konversi lain yang lebih mahal, seperti double ke int (yang bahkan memotong data). Bila tepat satu kandidat menang di lapisan terendah yang tersedia, pemanggilan sah; bila dua kandidat sama-sama menang, pemanggilan dinyatakan ambigu dan ditolak.

Beberapa implikasi praktisnya layak dipegang. Angka 42 bertipe int, jadi bila kita hanya menulis overload double, pemanggilan tampilkan(42) tetap sah lewat konversi — overload hilang bukan berarti pemanggilan gagal. Sebaliknya, menambah overload harus hati-hati: satu overload baru bisa mengubah pilihan pada PEMANGGILAN LAMA yang justru sebelumnya tepat. Daftar kandidat beserta alasannya selalu bisa kita intip dari pesan galat compiler, yang menampilkan tiap kandidat seperti pada contoh nanti.

Return Type Bukan Pembeda #

Aturan yang paling sering mengejutkan pemula: return type TIDAK ikut menentukan overload. Dua fungsi dengan parameter sama persis tetapi return type berbeda adalah konflik, bukan overload. Potongan berikut kami kompilasi sungguhan untuk menangkap penolakannya:

cpp Potongan bermasalah: hanya return type yang beda
int proses(int x) { return x; }
double proses(int x) { return x * 1.0; }

Pesan galat g++-nya: error: ambiguating new declaration of 'double proses(int)' dengan catatan old declaration 'int proses(int)'. Alasannya masuk akal: pemanggil bisa memanggil fungsi dan membuang hasilnya — proses(5); sebagai pernyataan — sehingga return type tidak selalu terlihat di titik panggil; compiler tidak punya dasar menentukan versi mana yang dimaksud. Kalau kebutuhan kita benar-benar menghasilkan tipe berbeda dari masukan yang sama, jalannya adalah nama berbeda, atau template yang kita singgung di ujung Section ini.

Overload dan Default Argument: Peluang Benturan #

Overload dan default argument sama-sama berusaha membuat pemanggilan lebih fleksibel — dan kombinasi keduanya bisa saling menggusur. Perhatikan dua fungsi berikut; keduanya sah di deklarasikan, tetapi pemanggilan sapa("Budi") membingungkan compiler:

cpp Potongan bermasalah: dua jalur untuk satu pemanggilan
void sapa(std::string nama) {
    std::cout << "Halo " << nama << "\n";
}

void sapa(std::string nama, std::string sapaan = "Halo") {
    std::cout << sapaan << ", " << nama << "\n";
}

// sapa("Budi");  // ambigu: kedua overload sama-sama bisa

Hasil kompilasinya: error: call of overloaded 'sapa(const char [5])' is ambiguous, disertai dua baris note: candidate: yang menunjuk kedua fungsi. Untuk pemanggilan satu argumen, overload pertama cocok langsung, sedangkan overload kedua cocok lewat bawaannya — dua pemenang berarti tidak ada pemenang. Pemanggilan sapa("Budi", "Selamat pagi") tetap sah karena hanya overload kedua yang mampu menerima dua argumen. Pelajarannya: bila satu parameter sudah punya nilai bawaan, jangan tambahkan overload dengan jumlah parameter yang saling menutupi — pilih salah satu gaya dan konsisten.

Rujukan resmi: cppreference: overload resolution dan cppreference: functions . Satu kata kunci kecil sering muncul di sekitar fungsi-fungsi kecil yang panas dipanggil: inline, topik Chapter berikutnya.