Di banyak program muncul kebutuhan yang terlihat sederhana: menyimpan puluhan jawaban “ya/tidak” dalam satu wadah ringkas — fitur aktif atau tidak, izin diberikan atau dicabut, sel sudah dilewati atau belum. Kita bisa menyusun std::vector<bool>, tetapi C++ punya wadah yang dirancang khusus untuk kebutuhan ini: std::bitset<N> (header <bitset>), deret tepat N bit yang ukurannya ditetapkan saat kompilasi. Setiap bit hanya bernilai 0 atau 1, dan bitset menyediakan operasi yang justru paling alami untuk bit: menyalakan, mematikan, membalik, menghitung, serta operasi bitwise antar dua bitset. Chapter ini berjalan dari operasi per-bit dasar, operasi bitwise antar bitset, hingga use case flag yang paling sering kita temui di kode nyata.

Deret Bit Berukuran Tetap #

std::bitset<8> membuat delapan bit yang semuanya bernilai 0 saat dibuat; std::bitset<32> membuat tiga puluh dua. Ukuran N adalah bagian dari tipe, jadi dua bitset dengan ukuran berbeda adalah tipe yang berbeda dan tidak bisa dicampur begitu saja. Konsekuensinya, bitset tidak bisa bertambah menyusut seperti vector — kebutuhan seperti itu justru tanda bahwa kita perlu std::vector<bool>, bukan bitset.

Cara membaca tampilannya penting dipahami sejak awal: ketika bitset dicetak, bit tertinggi (indeks N-1) berada di kiri dan bit indeks 0 berada di kanan. Jadi 00001001 berarti bit 0 dan bit 3 yang menyala — persis seperti menulis bilangan biner biasa. Metode to_string() menghasilkan deret karakter yang urutannya sama, dan operator<< mencetaknya tanpa perlu to_string().

Set, Reset, Test, Flip #

Program berikut mengoperasikan satu bitset<8> dari nol hingga konversi ke bilangan bulat. Jalankan dan perhatikan bagaimana posisi bit sesuai dengan urutan pencetakan yang baru kita bahas.

cpp
#include <bitset>    // std::bitset
#include <iostream>  // std::cout
#include <string>    // std::string

int main() {
    // bitset<8>: delapan bit berukuran tetap, semua bernilai 0 saat dibuat
    std::bitset<8> status;
    std::cout << "awal          : " << status.to_string() << "\n";

    // set(pos): nyalakan satu bit; set() tanpa argumen menyalakan semua
    status.set(0);
    status.set(3);
    std::cout << "set(0), set(3): " << status.to_string() << "\n";

    // test(pos): baca satu bit tanpa mengubahnya
    std::cout << "test(3)       : " << status.test(3) << "\n";
    std::cout << "test(5)       : " << status.test(5) << "\n";

    // flip(pos): balikkan satu bit; flip() tanpa argumen membalik semua
    status.flip(3);
    std::cout << "flip(3)       : " << status.to_string() << "\n";

    // operator[] juga bisa baca dan tulis
    status[6] = 1;
    std::cout << "status[6] = 1 : " << status.to_string() << "\n";

    // count: banyak bit menyala; any/none/all melaporkan kondisi keseluruhan
    std::cout << "count         : " << status.count() << "\n";
    std::cout << "any/none/all  : " << status.any() << " " << status.none()
              << " " << status.all() << "\n";

    // reset(pos): matikan satu bit; reset() tanpa argumen mematikan semua
    status.reset(0);
    std::cout << "reset(0)      : " << status.to_string() << "\n";

    // konversi dua arah: dari string dan ke bilangan bulat
    std::bitset<8> dariString(std::string("11000101"));
    std::cout << "dari string   : " << dariString.to_string()
              << " = " << dariString.to_ulong() << "\n";
    return 0;
}

Output yang Dihasilkan:

text
awal          : 00000000
set(0), set(3): 00001001
test(3)       : 1
test(5)       : 0
flip(3)       : 00000001
status[6] = 1 : 01000001
count         : 2
any/none/all  : 1 0 0
reset(0)      : 01000000
dari string   : 11000101 = 197

Bedah baris demi baris:

  1. Baris 7 (std::bitset<8> status;): delapan bit, semuanya 0. Tidak ada argumen konstruktor yang wajib — keadaan awalnya sudah jelas.
  2. Baris 11-12 (status.set(0); status.set(3);): menyalakan bit 0 dan bit 3, tercetak 00001001 — kiri adalah bit 7, kanan adalah bit 0.
  3. Baris 16-17 (status.test(3)): membaca satu bit tanpa mengubahnya: bit 3 menyala (1), bit 5 mati (0). status[3] setara untuk baca, tetapi test() juga memeriksa batas dan melempar exception bila indeks di luar N.
  4. Baris 20 (status.flip(3)): membalik bit 3 dari 1 menjadi 0; hasilnya 00000001.
  5. Baris 24 (status[6] = 1;): operator[] bisa menulis juga — bit 6 menyala, tercetak 01000001.
  6. Baris 28-29 (count, any/none/all): count() menghitung bit yang menyala (kini 2), any() bertanya “ada bit menyala?”, none() “semua mati?”, all() “semua menyala?”.
  7. Baris 32 (status.reset(0)): mematikan bit 0, menyisakan 01000000.
  8. Baris 36-37 (konversi): bitset bisa dibangun dari std::string berisi 0/1 dan dikonversi keluar: to_ulong() mengembalikan nilainya sebagai bilangan bulat — 11000101 bernilai 197.

Operasi Bitwise Antar bitset #

Dua bitset berukuran sama dapat dioperasikan langsung: & (AND), | (OR), ^ (XOR), dan ~ (NOT) bekerja per posisi bit, sementara << dan >> menggeser seluruh deret. Karena operasi ini milik bahasa sejak C dari awal, intuisinya langsung terbawa ke bitset.

cpp
#include <bitset>    // std::bitset
#include <iostream>  // std::cout
#include <string>    // std::string

int main() {
    // operasi bitwise antar dua bitset: &, |, ^, ~
    std::bitset<8> a(std::string("11001100"));
    std::bitset<8> b(std::string("10101010"));

    std::cout << "a        : " << a << "\n";
    std::cout << "b        : " << b << "\n";
    std::cout << "a & b    : " << (a & b) << "\n";
    std::cout << "a | b    : " << (a | b) << "\n";
    std::cout << "a ^ b    : " << (a ^ b) << "\n";
    std::cout << "~a       : " << (~a) << "\n";

    // geser bit: kiri menggandakan, kanan membagi dua (untuk bilangan)
    std::bitset<8> c(std::string("00001111"));
    std::cout << "c << 2   : " << (c << 2) << "\n";
    std::cout << "c >> 2   : " << (c >> 2) << "\n";

    // use case: kumpulan flag izin dalam satu bitset
    enum Posisi { Baca = 0, Tulis, Hapus };
    std::bitset<3> izin;
    izin[Baca] = true;
    izin[Tulis] = true;
    std::cout << "izin awal: " << izin << "\n";
    izin[Hapus] = true;   // diberi izin hapus
    izin[Tulis] = false;  // dicabut izin tulis
    std::cout << "izin kini: " << izin << " (Baca? " << izin[Baca]
              << ", Tulis? " << izin[Tulis] << ")\n";
    return 0;
}

Output yang Dihasilkan:

text
a        : 11001100
b        : 10101010
a & b    : 10001000
a | b    : 11101110
a ^ b    : 01100110
~a       : 00110011
c << 2   : 00111100
c >> 2   : 00000011
izin awal: 011
izin kini: 101 (Baca? 1, Tulis? 0)

Poin-poin yang perlu kita perhatikan:

  1. Baris 12-14 (&, |, ^): AND menyala hanya bila kedua bit menyala, OR bila salah satu menyala, XOR bila keduanya berbeda. Cocokkan baris demi baris dengan a dan b di atasnya.
  2. Baris 15 (~a): NOT membalik semua bit — kebalikan sempurna dari a.
  3. Baris 19-20 (geser): c << 2 menggeser deret ke arah bit tinggi, c >> 2 ke arah bit rendah; pada nilai biner itu setara menggandakan dan membagi dua per langkah geser.
  4. Baris 23-29 (flag izin): enum memberi nama untuk posisi bit — Baca bit 0, Tulis bit 1, Hapus bit 2. Memberi dan mencabut izin adalah sekadar menyalakan/mematikan satu bit, dan satu count() langsung melaporkan berapa izin aktif.

Kapan bitset Tepat Dipakai #

Bitset bersinar ketika jumlah flag diketahui sejak penulisan kode dan operasinya berbasis bit: status sel pada peta, tanda sudah-dikunjungi pada graf, kumpulan izin, atau filter sederhana. Operasi bitwise antar bitset membuat pembandingan sekumpulan flag jadi satu ekspresi — a & b menghasilkan flag yang menyala di keduanya tanpa loop manual. Untuk kebutuhan serupa dengan ukuran yang baru diketahui saat runtime, pakailah std::vector<bool>; untuk kebutuhan matematika bit yang lebih dalam, header <bit> menyediakan alat lanjutan di luar cakupan chapter ini.

Satu batas terakhir perlu diingat: bitset bukan container STL sejati — ia tidak punya iterator dan tidak bisa diiterasi dengan range-based for. Ia adalah tipe utilitas deret bit, dan perannya melengkapi container yang sudah kita kuasai. Chapter berikutnya beralih ke header <numeric>, yang menyimpan fungsi perhitungan kecil seperti gcd, lcm, dan midpoint — alat-alat pendek yang sering menyelamatkan kode sehari-hari.

Rujukan resmi: cppreference — std::bitset , cppreference — header <bitset> .