Kadang sebuah fungsi perlu mengembalikan lebih dari satu nilai: hasil bagi sekaligus sisanya, nama sekaligus umur, koordinat x sekaligus y. Membuat struct khusus untuk setiap kasus kecil itu terasa berlebihan, dan standard library menawarkan jalan tengahnya: std::pair<T1, T2> untuk pasangan dua nilai dan std::tuple<T...> untuk tiga nilai atau lebih. Keduanya bukan sekadar pembungkus — mereka diketahui oleh standard library itu sendiri; map menyimpan elemennya sebagai pair, dan banyak algorithm mengembalikan tuple. Chapter ini mengoperasikan keduanya, memakai structured bindings untuk membongkarnya, dan menimbang kapan mereka lebih baik daripada struct.
pair: Dua Nilai Berpasangan #
std::pair menyimpan anggota publik bernama first dan second. Ia sudah sering menyelinap lewat kode kita: kembalian insert pada container asosiatif adalah pair, dan setiap elemen std::map adalah pair<const Kunci, Nilai>.
#include <iostream> // std::cout
#include <string> // std::string
#include <tuple> // std::tuple, std::get, std::make_tuple, std::tuple_size_v
#include <utility> // std::pair, std::make_pair
// fungsi mengembalikan DUA nilai sekaligus lewat pair
std::pair<int, int> bagiDenganSisa(int a, int b) {
return {a / b, a % b};
}
// tuple memperluas ide yang sama untuk tiga nilai atau lebih
std::tuple<std::string, int, double> profilMahasiswa() {
return {"Budi", 21, 3.85};
}
int main() {
// --- pair: dua nilai berpasangan ---
std::pair<std::string, int> produk{"Kopi", 15000};
std::cout << "pair : " << produk.first
<< " - Rp" << produk.second << "\n";
// menerima kembalian pair dengan structured bindings
auto [hasil, sisa] = bagiDenganSisa(17, 5);
std::cout << "17 dibagi 5 : hasil=" << hasil
<< ", sisa=" << sisa << "\n";
// map menyimpan elemen berupa pair<const Kunci, Nilai>
std::pair<const std::string, int> entri{"teh", 30};
std::cout << "entri map : kunci=" << entri.first
<< ", nilai=" << entri.second << "\n";
// --- tuple: lebih dari dua tipe ---
auto profil = profilMahasiswa();
std::cout << "get<0> : " << std::get<0>(profil) << "\n";
std::cout << "get<1> : " << std::get<1>(profil) << "\n";
std::cout << "get<2> : " << std::get<2>(profil) << "\n";
// jumlah elemen tuple diketahui saat kompilasi
std::cout << "tuple_size_v : "
<< std::tuple_size_v<decltype(profil)> << "\n";
// structured bindings membongkar tuple tanpa std::get
auto [nama, umur, ipk] = profil;
std::cout << "bongkar : " << nama << ", " << umur
<< ", IPK " << ipk << "\n";
// make_pair dan make_tuple menyimpulkan tipe dari argumen
auto p = std::make_pair(std::string("apel"), 5);
auto t = std::make_tuple(1, 2.5, 'x');
std::cout << "make_pair : " << p.first << " x" << p.second << "\n";
std::cout << "make_tuple<2> : " << std::get<2>(t) << "\n";
// membandingkan pair dan tuple: urutan anggotanya yang dibandingkan
auto a = std::make_pair(1, 10);
auto b = std::make_pair(1, 20);
std::cout << "pair a < b : " << (a < b ? "ya" : "tidak") << "\n";
return 0;
}Output yang Dihasilkan:
pair : Kopi - Rp15000
17 dibagi 5 : hasil=3, sisa=2
entri map : kunci=teh, nilai=30
get<0> : Budi
get<1> : 21
get<2> : 3.85
tuple_size_v : 3
bongkar : Budi, 21, IPK 3.85
make_pair : apel x5
make_tuple<2> : x
pair a < b : yaMari kita bedah:
- Baris 7-9 (
bagiDenganSisa): fungsi mengembalikanstd::pair<int, int>dari dua perhitungan sekaligus — tanpa parameter keluaran (out parameter) dan tanpa struct tambahan. - Baris 23 (
auto [hasil, sisa] = ...): structured bindings langsung membongkar kembalian pair menjadi dua nama yang bermakna. Tanpa itu kita menulisstd::get<0>(...)danstd::get<1>(...)yang jauh lebih sulit dibaca. - Baris 28 (
pair<const std::string, int>): inilah tipe persis yang diberikan oleh iterasistd::map. Perhatikanconstpada kunci: map tidak mengizinkan kita mengubah kunci lewat iterasi, dan tipenya mewariskan aturan itu. - Baris 34-36 (
std::get<N>): elemen tuple diakses menurut indeks konstanta waktu kompilasi, bukan menurut nama.std::get<0>(profil)menghasilkanstd::string,std::get<1>menghasilkanint— tipe kembaliannya ikut mengikuti anggota yang diminta. - Baris 39-40 (
tuple_size_v):std::tuple_size_v<decltype(profil)>adalah compile-time query yang menghasilkan3. Ini berguna dalam kode generik yang perlu tahu panjang tuple tanpa menjalankannya. - Baris 48-51 (
make_pair/make_tuple): kedua fungsi murni kemudahan — tipe argumen disimpulkan otomatis, sehingga kita tidak menulis tipe panjang secara eksplisit. Sejak C++17, sintaksauto p = pair{...}dan pengurangan argumen template menjadikanmake_*lebih jarang dibutuhkan, tetapi kita tetap akan menemukannya di kode lama. - Baris 56 (
a < b): pair dan tuple punya perbandingan bawaan yang bekerja leksikografis: bandingkanfirstdulu; bila sama, bandingkansecond. Karenafirstkeduanya sama-sama1, keputusan jatuh kesecond—10 < 20menghasilkanya.
pair, tuple, atau struct? #
Ketiganya mengelompokkan data, jadi pertimbangannya soal kejelasan:
| Pilihan | Kelebihan | Kekurangan |
|---|---|---|
std::pair / std::tuple | ringkas, didukung penuh standard library | anggota bernama first/get<0> — makna mudah kabur |
struct | anggota bernama (nama, umur) — paling terbaca | perlu definisi sendiri |
Aturan yang sehat: pair/tuple untuk nilai yang maknanya jelas dari konteks dan umur pendek (kembalian fungsi internal, elemen map); struct untuk data yang menyeberangi batas modul atau punya lebih dari tiga anggota. Kode yang membaca std::get<2>(hasil) dalam jumlah besar adalah sinyal untuk beralih ke struct.
Pair dan tuple mengelompokkan nilai yang semuanya ada. Berbeda dengannya, chapter berikutnya membahas nilai yang mungkin tidak ada sama sekali: std::optional, jawaban standar untuk fungsi yang bisa gagal.
Rujukan resmi: cppreference — std::pair , cppreference — std::tuple , cppreference — std::get .