Kode kita sudah sering bertukar nilai antar tipe tanpa kita sadari: char masuk ke int, int bergabung dengan double, dan compiler hanya diam dan menuruti. Proses itu disebut konversi tipe (type conversion), dan C++ menjalankannya di dua jalur: secara implisit — otomatis oleh compiler mengikuti aturan standar — atau secara eksplisit lewat static_cast ketika kita yang memerintahkan. Jalur implisit menghemat penulisan tetapi kadang membuang data tanpa suara; jalur eksplisit sedikit lebih panjang, tetapi niatnya terdokumentasi di kode. Chapter ini memetakan kapan konversi aman, kapan kehilangan data, dan mengapa static_cast menjadi alat pilihan C++ modern.
Konversi Implisit dan Integer Division #
Aturan bakunya: konversi dari tipe “kecil” ke tipe “besar” (misalnya int ke double) biasanya aman karena tidak ada nilai yang hilang; sebaliknya, konversi dari tipe besar ke kecil bisa memotong data. Compiler juga menerapkan promotion: dalam ekspresi campuran, char dan short otomatis naik menjadi int, dan saat int bertemu double, keduanya dikerjakan dalam double. Program berikut memperlihatkan akibat praktisnya — termasuk satu jebakan terkenal bernama integer division:
#include <iostream>
int main() {
// Integer division dulu, baru hasilnya disimpan ke double
double rataSalah = 19 / 4; // 19 / 4 = 4 (int), lalu jadi 4.0
double rataBenar = 19.0 / 4; // promotion: 4 naik kelas menjadi double
// static_cast: menyatakan konversi secara eksplisit
int totalPoin = 17;
int jumlahSiswa = 4;
double rataPoin = static_cast<double>(totalPoin) / jumlahSiswa;
// Konversi implisit saat assignment: double -> int terpotong
int implisit = 3.99; // lolos tanpa suara: 3
// static_cast dengan niat yang sama, kini terlihat gamblang
int eksplisit = static_cast<int>(3.99);
// C-style cast: gaya lama warisan bahasa C
double gayaLama = (double)totalPoin / jumlahSiswa;
std::cout << "19 / 4 = " << rataSalah << "\n";
std::cout << "19.0 / 4 = " << rataBenar << "\n";
std::cout << "static_cast<double> = " << rataPoin << "\n";
std::cout << "implisit 3.99 -> = " << implisit << "\n";
std::cout << "eksplisit 3.99 -> = " << eksplisit << "\n";
std::cout << "(double) totalPoin = " << gayaLama << "\n";
return 0;
}Output yang Dihasilkan:
19 / 4 = 4
19.0 / 4 = 4.75
static_cast<double> = 4.25
implisit 3.99 -> = 3
eksplisit 3.99 -> = 3
(double) totalPoin = 4.25Baris demi baris yang menentukan:
- Baris 7 (
double rataSalah = 19 / 4;): pembagian duaintdilakukan lebih dulu dalam dunia integer, menghasilkan4(sisa 3 dibuang), baru kemudian disimpan kedoublesebagai4.0. Tipe tujuan tidak mengubah perhitungan yang sudah selesai — inilah jebakan integer division yang klasik. - Baris 8 (
19.0 / 4): satu operand dibuatdouble, sehingga promotion menaikkan4menjadi4.0dan pembagian berjalan pecahan:4.75. - Baris 12 (
static_cast<double>(totalPoin) / jumlahSiswa): solusi resmi C++ untuk kasus di atas — kita memintatotalPoindidobelkan sebelum dibagi, menghasilkan rata-rata benar4.25. - Baris 15 vs 18: kedua baris menghasilkan
3dari3.99. Yang pertama terjadi tanpa jejak di kode; yang kedua menuliskan niatnya dengan gamblang — pembaca langsung tahu pemotongan itu disengaja, bukan bug.
static_cast: Menyatakan Konversi secara Eksplisit #
Sintaksnya agak panjang: static_cast<tipe_tujuan>(nilai). Bentuk kata kerja ini disengaja — konversi yang berisiko harus terlihat seperti apa adanya, mudah dicari dengan teks static_cast, dan tidak tersamar dengan panggilan fungsi biasa. static_cast adalah konversi yang diaudit compiler: hanya aturan konversi yang memang sah menurut standar yang boleh lewat, dan bila konversinya mustahil (misalnya static_cast<int>("teks")), compiler menolaknya alih-alih diam-diam menambahkan perilaku aneh.
Perlu diketahui ada empat bentuk cast di C++ modern — static_cast, const_cast, reinterpret_cast, dan dynamic_cast — masing-masing dengan wewenang berbeda; tiga terakhir baru masuk akal setelah kita mempelajari pointer, class, dan inheritance di section-section berikutnya. Untuk semua kebutuhan konversi tipe biasa, static_cast adalah jawabannya.
C-Style Cast dan Mengapa Dihindari #
Baris terakhir program memakai (double)totalPoin — bentuk warisan bahasa C yang masih sah dan banyak ditemui di kode lama. Masalahnya, cast gaya C ini “serba bisa”: compiler mencoba serangkaian aturan konversi secara diam-diam hingga ada yang lolos, termasuk konversi berbahaya yang seharusnya memakai reinterpret_cast. Karena itu satu baris (T)x bisa menyembunyikan niat yang sangat berbeda, dan pencarian teks (T) di kode juga lebih mudah meleset. Guideline modern (termasuk C++ Core Guidelines
) menyarankan memilih bentuk bernama: eksplisit, dapat diaudit, dan sengaja dibuat tidak nyaman supaya dipakai seperlunya.
Kapan Aman, Kapan Kehilangan Data #
| Arah konversi | Status | Catatan |
|---|---|---|
char/short → int | aman | promotion; tidak ada nilai yang hilang |
int → double | hampir aman | bilangan besar (lebih dari 2⁵³) mulai kehilangan ketelitian |
float → double | aman | presisi justru bertambah |
double → int | kehilangan data | bagian pecahan dibuang (truncation), bukan dibulatkan |
double → float | kehilangan presisi | digit di luar kapasitas float dibulatkan |
long long → int | kehilangan data | bit di luar 32 bit dibuang; bisa berubah total |
Tabel ini menegaskan pola besarnya: naik kelas biasanya selamat, turun kelas perlu alasan yang tertulis. Bagian “kecil ke besar” pun punya catatan kaki untuk unsigned yang sudah kita kenal: int negatif yang dikonversi ke unsigned int menghasilkan angka raksasa — perilaku terdefinisi, tetapi hampir pasti bukan yang kita maksud.
Salah satu konversi paling berisiko dari tabel di atas — konversi yang membuat nilai berubah diam-diam — sebenarnya bisa dimatikan total oleh compiler. Mekanismenya menyangkut bentuk inisialisasi kurung kurawal dari Chapter Inisialisasi Variabel, dan itulah topik penuh Chapter berikutnya: narrowing conversion.
Rujukan resmi untuk topik Chapter ini: implicit conversions dan explicit type conversion di cppreference.