Topik ini membahas tiga kata kunci yang mengatur tipe dan ketetapan nilai, yaitu auto, const, dan constexpr. Kata kunci auto memindahkan tugas menuliskan tipe kepada compiler, sedangkan const dan constexpr menetapkan bahwa sebuah nilai tidak boleh berubah dan menentukan kapan nilainya diketahui. Ketiganya diperagakan dengan program kecil, lengkap dengan pesan error yang muncul ketika aturannya dilanggar.
auto dan type inference#
Kata kunci auto meminta compiler menentukan tipe variabel dari nilai inisialisasinya. Mekanisme ini disebut type inference, yaitu penurunan tipe secara otomatis oleh compiler. Variabel yang dideklarasikan dengan auto tetap bertipe kuat, hanya saja tipe tersebut diturunkan alih-alih ditulis.
#include <iostream>
int main() {
auto jumlah = 42; // int
auto harga = 12.5; // double
auto inisial = 'D'; // char
auto aktif = true; // bool
std::cout << jumlah << " " << harga << " " << inisial << " " << aktif << "\n";
return 0;
}42 12.5 D 1Aturan penurunannya mengikuti litera yang diberikan, sehingga 42 menghasilkan int, 12.5 menghasilkan double, 'D' menghasilkan char, dan true menghasilkan bool. Deklarasi auto tanpa inisialisasi ditolak compiler karena tidak ada dasar untuk menurunkan tipe. Pemakaian terbaiknya adalah pada tipe yang panjang atau jelas dari konteks, sedangkan untuk variabel penting yang tipe turunannya membingungkan, menulis tipe secara eksplisit tetap lebih terbaca.
Jebakan auto pada litera string dan ekspresi#
auto mengikuti nilai inisialisasinya apa adanya, termasuk ketika hasil penurunan tipenya tidak diharapkan. Program berikut memuat dua kasus yang paling sering mengejutkan, yaitu litera string dan pembagian bilangan bulat.
#include <iostream>
int main() {
auto sapaan = "Halo";
auto hasil = 7 / 2;
std::cout << "sapaan = " << sapaan << "\n";
std::cout << "sizeof(sapaan) = " << sizeof(sapaan) << "\n";
std::cout << "hasil = " << hasil << "\n";
return 0;
}sapaan = Halo
sizeof(sapaan) = 8
hasil = 3Litera "Halo" bukan std::string, melainkan deret karakter yang diturunkan menjadi const char*. Ukuran 8 pada platform 64 bit menegaskan bahwa sapaan adalah pointer. Konsekuensinya, ekspresi sapaan.length() ditolak compiler dengan pesan request for member 'length' in 'sapaan', which is of non-class type 'const char*'. Kasus kedua, 7 / 2 adalah pembagian dua bilangan bulat sehingga hasilnya 3, dan auto hanya mencatat hasil itu. Untuk string, deklarasi eksplisit std::string sapaan = "Halo"; memberi tipe yang benar, sedangkan pembagian yang mengharapkan pecahan membutuhkan operan bertipe pecahan.
const: variabel yang tidak boleh berubah#
Kata kunci const menandai variabel yang nilainya tidak boleh diubah setelah inisialisasi. Variabel semacam ini wajib diinisialisasi saat dideklarasikan, dan setiap percobaan penugasan ulang ditolak compiler.
#include <iostream>
int main() {
const double tarifPerKm = 4500.0;
double jarak = 12.5;
double biaya = tarifPerKm * jarak;
std::cout << "Biaya perjalanan: " << biaya << "\n";
// tarifPerKm = 5000.0; // ditolak compiler: tarifPerKm tidak boleh diubah
return 0;
}Biaya perjalanan: 56250Baris penugasan yang dinonaktifkan lewat komentar menunjukkan pola pelanggarannya. Apabila komentar itu dilepas, compiler menolak programnya dengan pesan error: assignment of read-only variable 'tarifPerKm'. Pesan semacam ini muncul saat kompilasi, sehingga pelanggaran tertangkap sebelum program berjalan.
// tidak-dijalankan (baris penugasan ditolak compiler, pesannya dijelaskan di prosa)
const int stok = 10;
stok = 20; // error: assignment of read-only variable 'stok'
Kebiasaan menandai variabel yang memang tidak berubah disebut const-correctness, yaitu pola pikir yang menyerahkan pemeriksaan ketetapan kepada compiler. Semakin banyak nilai yang ditandai const, semakin sedikit tempat bagi kesalahan penugasan yang tidak disengaja.
constexpr: dihitung saat kompilasi#
Kata kunci constexpr menandai variabel atau fungsi yang nilainya dihitung saat kompilasi (compile-time), bukan saat program berjalan. Variabel constexpr otomatis bersifat const, tetapi keduanya tidak sama. Variabel const hanya menjamin tidak berubah, sedangkan nilai constexpr wajib diketahui pada saat kompilasi.
#include <iostream>
constexpr int ukuranMaksimum = 100;
constexpr int kuadrat(int x) {
return x * x;
}
int main() {
constexpr int sisi = 12;
constexpr int luas = kuadrat(sisi); // dihitung saat kompilasi
int data[ukuranMaksimum] = {};
data[0] = luas;
std::cout << "kuadrat(" << sisi << ") = " << data[0] << "\n";
std::cout << "jumlah elemen data = " << sizeof(data) / sizeof(data[0]) << "\n";
return 0;
}kuadrat(12) = 144
jumlah elemen data = 100Fungsi kuadrat dideklarasikan constexpr, sehingga pemanggilannya dengan argumen yang diketahui saat kompilasi menghasilkan nilai 144 yang dihitung oleh compiler. Kebutuhan nyata dari sifat ini terlihat pada array data, karena ukuran array wajib diketahui saat kompilasi dan hanya nilai constexpr yang memenuhinya. Nilai yang diperoleh dari masukan pengguna atau dari berkas tidak dapat menjadi constexpr.
Fungsi constexpr dengan argumen saat berjalan#
Kata constexpr pada fungsi adalah janji kemampuan, bukan tuntutan, sehingga fungsi yang sama tetap dapat dipanggil dengan nilai yang baru diketahui saat program berjalan. Pemanggilan dengan argumen constexpr dihitung saat kompilasi, sedangkan pemanggilan dengan argumen biasa dihitung saat program berjalan. Program berikut memanggil satu fungsi yang sama dengan kedua cara tersebut.
#include <iostream>
constexpr int kuadrat(int x) {
return x * x;
}
int main() {
int masukan = 9;
int hasilBerjalan = kuadrat(masukan);
constexpr int hasilKompilasi = kuadrat(9);
std::cout << "kuadrat(9) saat berjalan = " << hasilBerjalan << "\n";
std::cout << "kuadrat(9) saat kompilasi = " << hasilKompilasi << "\n";
return 0;
}kuadrat(9) saat berjalan = 81
kuadrat(9) saat kompilasi = 81Kesalahan sebaliknya terjadi ketika hasil hitung saat kompilasi dituntut dari nilai yang baru diketahui saat program berjalan. Deklarasi constexpr int z = kuadrat(masukan); ditolak compiler dengan pesan the value of 'masukan' is not usable in a constant expression, karena masukan bukan konstanta. Mekanisme dua arah inilah yang membuat satu fungsi constexpr melayani dua kebutuhan sekaligus, yaitu konstanta yang diperiksa saat kompilasi dan perhitungan biasa saat program berjalan.
Memilih antara const dan constexpr#
Kedua kata kunci berdiri di titik yang berbeda. const menjawab pertanyaan apakah nilai boleh berubah, sedangkan constexpr menjawab pertanyaan kapan nilai diketahui. Tabel berikut merangkum perbedaannya.
| Aspek | const | constexpr |
|---|---|---|
| Perubahan nilai | Ditolak | Ditolak |
| Waktu nilai diketahui | Saat kompilasi atau saat berjalan | Wajib saat kompilasi |
| Bisa diinisialisasi dari masukan pengguna | Boleh | Tidak boleh |
| Layak untuk ukuran array | Hanya bila nilainya juga diketahui saat kompilasi | Selalu |
Praktik yang disarankan mulai dari pertanyaan sederhana. Apabila nilainya berasal dari perhitungan yang seluruh masukannya sudah diketahui saat kompilasi, pilih constexpr. Apabila nilainya diperoleh saat program berjalan tetapi tidak boleh berubah setelahnya, pilih const.
Kesalahan umum#
Tiga kekeliruan berikut paling sering muncul ketika ketiga kata kunci ini mulai digabungkan.
- Menulis
auto x;tanpa inisialisasi. Compiler melaporkanerror: declaration of 'auto x' has no initializer, karena tipe tidak mungkin diturunkan tanpa nilai. Perbaikannya memberi inisialisasi pada deklarasi yang sama. - Mengira
constcukup untuk menetapkan ukuran array dari nilai yang baru diketahui saat berjalan. Pada konfigurasi default GCC, deklarasi semacam itu lolos hanya karena ekstensi array berpanjang variabel, sehingga kode yang sama ditolak compiler lain. Ukuran array yang portabel membutuhkan nilai yang diketahui saat kompilasi, misalnyaconstexpr. - Menggunakan
constexpruntuk nilai yang diperoleh dari masukan pengguna atau berkas. Deklarasinya ditolak karena nilainya tidak mungkin diketahui saat kompilasi, dan perbaikannya menggunakanconstbiasa.
Ringkasan#
automenurunkan tipe variabel dari inisialisasinya, dan variabelnya tetap wajib diinisialisasi karena tipe tidak dapat ditebak tanpa nilai.constmelarang perubahan nilai setelah inisialisasi, dan setiap pelanggarannya ditolak compiler dengan pesanassignment of read-only variable.constexprmenuntut nilai dihitung saat kompilasi, sehingga hanya nilai tersebut yang boleh dipakai sebagai ukuran array.constmengatur kebolehan berubah, sedangkanconstexprmengatur waktu nilai diketahui, dan keduanya dapat dipandu dengan tabel di atas.
Referensi#
- auto specifier — cppreference — Aturan type inference untuk variabel yang dideklarasikan dengan auto.
- const/volatile qualifiers — cppreference — Dokumentasi kualifikasi const beserta aturan inisialisasinya.
- constexpr specifier — cppreference — Definisi resmi variabel dan fungsi constexpr dalam standar C++.
- Constant variables and named constants — LearnCpp — Panduan bertahap tentang variabel const dan konstanta bernama.
- Type deduction for objects using the auto keyword — LearnCpp — Pembahasan aturan penurunan tipe dengan auto.
- constexpr variables — LearnCpp — Penjelasan variabel constexpr dan perhitungan saat kompilasi.