Topik ini membahas operator konversi pada tipe kelas, yaitu fungsi yang mengubah objek menjadi tipe lain, bersama kata kunci explicit yang mengendalikan kapan konversi boleh terjadi. Pembahasan mencakup dua arah konversi, kejutan yang muncul dari konversi implisit, dan cara menutup celahnya. Contohnya memakai kelas Pecahan yang dikonversi menjadi double.
Dua arah konversi tipe buatan sendiri#
Konversi menuju kelas diberikan lewat constructor yang menerima satu parameter, misalnya Pecahan(int) yang mengubah bilangan bulat menjadi pecahan. Konversi dari kelas menuju tipe lain diberikan lewat conversion operator, yaitu fungsi yang mengubah objek menjadi tipe lain. Sintaksnya berbeda dari fungsi biasa: namanya berupa kata kunci operator diikuti nama tipe tujuan, sedangkan tipe baliknya tidak ditulis karena sudah tersirat pada namanya.
Deklarasi lengkapnya berupa operator double() const tanpa parameter. Sifat const penting karena konversi tidak seharusnya mengubah objek sumbernya. Setelah operator ini ada, objek Pecahan dapat muncul di tempat mana pun yang meminta double, mulai dari penugasan, aritmetika, sampai pemanggilan fungsi. Kemudahan inilah yang sekaligus menjadi sumber masalah pada bagian berikutnya.
Sifat constructor berparameter tunggal perlu ditinjau pada waktu yang sama. Constructor Pecahan(int) yang tidak ditandai explicit membuat penugasan seperti Pecahan p = 3; dianggap sah, dan fungsi yang meminta Pecahan dapat menerima int tanpa terlihat di titik pemanggilannya. Kelas Suhu dengan constructor Suhu(double) mengalami hal serupa, karena bilangan apa pun dapat berubah menjadi suhu tanpa peringatan. Dua arah konversi ini menentukan seberapa mudah sebuah tipe berubah bentuk tanpa disadari, sehingga keduanya perlu dikendalikan dengan cara yang sama.
Operator konversi implisit dan kejutannya#
Program berikut mendefinisikan operator double() tanpa explicit, sehingga compiler menerapkannya secara diam-diam. Jalankan programnya dan cermati baris mana saja yang sesungguhnya memicu konversi.
#include <iostream>
class Pecahan {
public:
Pecahan(int pembilang, int penyebut) : pembilang(pembilang), penyebut(penyebut) {}
// operator konversi: Pecahan dapat berubah menjadi double secara implisit
operator double() const {
return static_cast<double>(pembilang) / penyebut;
}
int getPembilang() const { return pembilang; }
int getPenyebut() const { return penyebut; }
private:
int pembilang;
int penyebut;
};
void tampilkanNilai(double nilai) {
std::cout << "nilai: " << nilai << "\n";
}
int main() {
Pecahan p(1, 2);
Pecahan nol(0, 5);
double x = p; // konversi implisit ke double
std::cout << "x = " << x << "\n";
std::cout << "p dicetak langsung: " << p << "\n";
tampilkanNilai(p);
if (p) {
std::cout << "p dianggap bernilai bukan nol\n";
}
if (!nol) {
std::cout << "nol dianggap bernilai nol\n";
}
return 0;
}x = 0.5
p dicetak langsung: 0.5
nilai: 0.5
p dianggap bernilai bukan nol
nol dianggap bernilai nolKeluarannya memperlihatkan beberapa konteks yang semuanya mengonversi tanpa diminta. Baris pertama memang disengaja, tetapi baris p dicetak langsung: 0.5 memperlihatkan bahwa std::cout memilih mencetak hasil konversi double, bukan bentuk pecahan yang dimaksudkan. Pemanggilan tampilkanNilai(p) juga lolos tanpa peringatan apa pun, dan kondisi if (p) mengubah objek menjadi nilai bool lewat double. Semua konversi ini sah menurut aturan bahasa, sehingga compiler tidak menyampaikan pesan apa pun.
Konversi implisit turut bekerja saat pemilihan overload. Apabila tersedia fungsi proses(int) dan fungsi proses(double), pemanggilan proses(p) memilih versi double tanpa kesempatan menolak. Rantai aritmetika seperti p + 0.25 pun dievaluasi dalam double, sehingga hasilnya bukan lagi Pecahan melainkan bilangan mengambang. Bagi tipe yang maknanya bergantung pada representasinya, seperti pecahan, kehilangan representasi tersebut mudah luput dari pengamatan.
Mengendalikan konversi dengan explicit#
Kata kunci explicit ditempatkan di depan deklarasi operator konversi dan membatasi konversi pada konteks yang memintanya secara langsung. Konteks tersebut mencakup cast bernama seperti static_cast<double>(p) dan inisialisasi langsung (direct-initialization) seperti double y(p). Konteks konversi implisit seperti pemanggilan fungsi dan pencetakan langsung ditolak compiler. Constructor berparameter tunggal dapat diberi explicit dengan cara yang sama untuk mengendalikan arah konversi menuju kelas.
Program berikut menandai operator double() dengan explicit dan menambahkan operator bool() yang juga explicit untuk pengujian keadaan. Baris yang memicu error sengaja ditulis sebagai komentar agar programnya tetap dapat dikompilasi.
#include <iostream>
class Pecahan {
public:
Pecahan(int pembilang, int penyebut) : pembilang(pembilang), penyebut(penyebut) {}
// explicit: konversi hanya terjadi bila diminta secara langsung
explicit operator double() const {
return static_cast<double>(pembilang) / penyebut;
}
// explicit operator bool untuk pengujian keadaan di kondisi if
explicit operator bool() const {
return pembilang != 0;
}
int getPembilang() const { return pembilang; }
int getPenyebut() const { return penyebut; }
private:
int pembilang;
int penyebut;
};
void tampilkanNilai(double nilai) {
std::cout << "nilai: " << nilai << "\n";
}
int main() {
Pecahan p(1, 2);
double x = static_cast<double>(p); // konversi diminta secara eksplisit
std::cout << "x = " << x << "\n";
double y(p); // direct-initialization tetap mengizinkan konversi explicit
std::cout << "y = " << y << "\n";
// std::cout << p; // error: konversi implisit ke double sudah tidak ada
// tampilkanNilai(p); // error: pun demikian
if (p) { // kondisi if menerima explicit operator bool lewat konversi kontekstual
std::cout << "p dianggap bernilai bukan nol\n";
}
return 0;
}x = 0.5
y = 0.5
p dianggap bernilai bukan nolBaris std::cout << p yang sebelumnya mencetak 0.5 kini gagal dikompilasi apabila komentarnya dibuka, karena tidak ada lagi konversi implisit yang cocok dengan overload operator<<. Adapun if (p) tetap berhasil karena kondisi if memakai konversi kontekstual (contextual conversion) ke bool. Konversi kontekstual tersebut hanya mempertimbangkan operator yang tipe baliknya persis bool, itulah sebabnya operator double() yang explicit tidak ikut dipakai di kondisi if.
Kombinasi inilah yang menjadikan explicit operator bool pola standar, dan pustaka standar memakainya pada tipe seperti std::unique_ptr. Pesan error yang muncul pun berisi ketidakcocokan konversi yang jelas, sehingga lebih mudah ditindaklanjuti daripada perilaku senyap sebelumnya.
Kesalahan umum#
- Menulis dua operator konversi ke tipe yang berdekatan, misalnya ke
doubledan kefloat. Compiler menemukan dua kandidat yang sama-sama perlu konversi, sehingga ekspresi tertentu berakhir sebagai error ambiguitas. Cukup satu konversi ke tipe yang paling umum, sisanya ditangani konversi standar. - Melupakan
constpada operator konversi. Operator konversi tidak mengubah objeknya, sehingga objekconsttidak dapat dikonversi dan pemanggilannya gagal. Tambahkanconstpada deklarasinya. - Membuat
operator booltanpaexplicit. Pencetakan langsung sepertistd::cout << pmemilih overloadbooldan mencetak1alih-alih isi objek. Tandai denganexplicitagar konversinya hanya dipakai di konteks yang meminta. - Mengizinkan konversi menuju kelas tanpa
explicitpada constructor berparameter tunggal. ConstructorSuhu(double)yang implisit membuat penugasanSuhu s = 36.5;sah, tetapi juga membuat pemanggilan yang salah tipe lolos tanpa peringatan. Tandai denganexplicitbila konversinya tidak diminta secara jelas.
Ringkasan#
- Constructor berparameter tunggal memberi konversi menuju kelas, sedangkan operator konversi memberi konversi dari kelas ke tipe lain.
- Konversi implisit diterapkan compiler pada banyak konteks dan dapat mengubah makna program tanpa peringatan.
explicitmembatasi konversi pada cast bernama dan inisialisasi langsung, sementaraexplicit operator booltetap dipakai di kondisiifmelalui konversi kontekstual.
Referensi#
- User-defined conversion — cppreference — aturan lengkap operator konversi dan constructor konversi.
- explicit specifier — cppreference
— perilaku
explicitpada constructor dan operator konversi. - 14.16 — Converting constructors and the explicit keyword — LearnCpp
— pembahasan constructor konversi dan pengendaliannya dengan kata kunci
explicit.