std::variant<A, B> menampung salah satu dari daftar tipe yang kita tulis sendiri — daftar yang tertutup. Ada situasi yang justru kebalikannya: fungsi menyimpan “nilai apa pun” tanpa tahu sejak awal tipenya, misalnya metadata plugin, properti konfigurasi, atau data hasil parsing yang bentuknya baru diketahui saat runtime. Untuk kebutuhan terbuka itulah <any> menyediakan std::any: wadah yang bisa menampung tipe apa pun yang bisa disalin, lengkap dengan pemeriksaan tipe yang aman. Kebebasan ini ada harganya — pemeriksaan tipe dan pengambilan nilai dilakukan pada runtime — sehingga any adalah alat terakhir dalam daftar, dipakai ketika variant tidak memungkinkan. Chapter ini mengoperasikan siklus hidup any lengkap dan menimbang kapan ia layak dipilih.
Wadah Serbaguna dan Aturan Mainnya #
Siklus hidup std::any sederhana: mulai kosong, diisi dengan tipe apa pun (mengganti isi berarti menghapus isi lama), diperiksa lewat has_value() dan type(), dibaca lewat std::any_cast, dan bisa dikosongkan lagi dengan reset(). Dua bentuk any_cast penting dibedakan: bentuk by value any_cast<T>(a) melempar std::bad_any_cast bila tipe tidak cocok, sedangkan bentuk pointer any_cast<T>(&a) mengembalikan nullptr bila tidak cocok — bentuk kedua inilah yang kita pakai agar contoh tetap berjalan tanpa exception (materi Section Error Handling).
#include <any> // std::any, std::any_cast
#include <iostream> // std::cout
#include <string> // std::string
int main() {
std::any kotak; // kosong saat lahir
std::cout << "ada isi? : " << kotak.has_value() << "\n";
// tampung int
kotak = 42;
std::cout << "isi int : " << std::any_cast<int>(kotak) << "\n";
std::cout << "tipe == int? : " << (kotak.type() == typeid(int)) << "\n";
// ganti ke string
kotak = std::string("teks bebas");
std::cout << "isi string : " << std::any_cast<std::string>(kotak) << "\n";
std::cout << "tipe == string?: "
<< (kotak.type() == typeid(std::string)) << "\n";
// ganti ke double
kotak = 3.5;
std::cout << "tipe == double?: " << (kotak.type() == typeid(double)) << "\n";
// any_cast lewat pointer: tidak melempar exception, kembalian nullptr bila salah tipe
if (int* p = std::any_cast<int>(&kotak)) {
std::cout << "int : " << *p << "\n";
} else {
std::cout << "int : nullptr (bukan int)\n";
}
if (double* d = std::any_cast<double>(&kotak)) {
std::cout << "double : " << *d << "\n";
}
// reset: kembalikan kotak ke keadaan kosong
kotak.reset();
std::cout << "setelah reset : ada isi? "
<< (kotak.has_value() ? "ya" : "tidak") << "\n";
return 0;
}Output yang Dihasilkan:
ada isi? : 0
isi int : 42
tipe == int? : 1
isi string : teks bebas
tipe == string?: 1
tipe == double?: 1
int : nullptr (bukan int)
double : 3.5
setelah reset : ada isi? tidakMari kita bedah:
- Baris 6-8 (
std::any kotak;): any yang baru dibuat kosong;has_value()melapor0(false). Bandingkan denganvariantyang selalu berisi alternatif pertamanya. - Baris 11-13 (
kotak = 42): penugasan menyalin nilaiintke dalam any.any_cast<int>(kotak)membaca kembali nilainya — bentuk by value ini akan melemparstd::bad_any_castbila kita meminta tipe yang salah. - Baris 13, 17, 19 (
type() == typeid(...)):type()mengembalikan informasi tipe runtime yang bisa kita bandingkan dengantypeid. Perbandingan menghasilkanbooldeterministik — berbeda dari mencetaktype().name(), yang nama hasilnya berbeda antar compiler (di g++ berupa nama singkat sepertiiuntuk int). - Baris 14-16 (
kotak = std::string(...)): mengganti isi dari int ke string. Objek int lama dihapus, string baru disalin masuk — any mengurus kedua langkah itu. - Baris 25-30 (
any_castbentuk pointer):any_cast<int>(&kotak)mengembalikanint*bila isi memang int, ataunullptrbila bukan. Karena isinya saat itudouble, percobaan pertama jatuh ke cabangelse— pemeriksaan aman tanpa exception. - Baris 36-38 (
reset()): mengosongkan kembali kotak;has_value()kembali0dan sumber daya milik isi lama dibebaskan.
Biaya yang Kita Bayar #
std::any bukan tipe gratis. Ia menyimpan nilai di memori internal (kadang small buffer, kadang alokasi heap bila nilainya besar), mengingat tipenya lewat mekanisme runtime, dan setiap pembacaan wajib melewati pemeriksaan any_cast. Ditambah lagi, compiler tidak bisa memeriksa kesalahan tipe pada waktu kompilasi — kesalahan hanya terungkap saat program berjalan. Karena itu urutan pertimbangan kita sebaiknya selalu: struct bila bentuknya pasti, optional bila mungkin kosong, variant bila pilihannya terbatas, dan any hanya bila daftar tipenya benar-benar terbuka.
| Pertanyaan | Alat yang cocok |
|---|---|
| “Tipe pasti, tapi mungkin tidak ada” | std::optional<T> |
| “Salah satu dari tipe A, B, C (tertulis)” | std::variant<A, B, C> |
| “Tipe apa pun, belum diketahui saat menulis kode” | std::any |
Dengan tiga tipe utilitas nilai ini tuntas, kita beralih ke utilitas yang bekerja pada kumpulan data: std::span, jendela tanpa kepemilikan ke data berurutan.
Rujukan resmi: cppreference — std::any , cppreference — any_cast .