Bỏ qua để đến nội dung

std::vector<bool> là trường hợp đặc biệt

std::vector<bool> được cài đặt khác hoàn toàn so với mọi std::vector<T> khác - đây là một trong những quyết định thiết kế gây tranh cãi nhiều nhất trong lịch sử C++, và thường khiến người mới học bối rối.

#include <vector>
#include <iostream>
int main() {
std::vector<bool> flags = {true, false, true};
bool& ref = flags[0]; // KHÔNG BIÊN DỊCH ĐƯỢC như mong đợi!
return 0;
}

Để tiết kiệm bộ nhớ, vector<bool> không lưu trữ mỗi bool trong một byte riêng như các kiểu khác - nó “nén” 8 giá trị bool vào 1 byte duy nhất, mỗi giá trị chỉ chiếm 1 bit. Điều này có nghĩa flags[0] không thể trả về một bool& thực sự (không có “một byte bool” nào tồn tại để tham chiếu đến) - nó trả về một đối tượng proxy giả lập hành vi của tham chiếu.

std::vector<bool> flags = {true, false, true};
auto x = flags[0]; // x KHÔNG phải bool, mà là std::vector<bool>::reference (một proxy object!)
for (bool b : flags) { // Vẫn hoạt động vì phép chuyển đổi ngầm định từ proxy sang bool
std::cout << b << " ";
}

Cạm bẫy: auto không cho ra kiểu bạn nghĩ

Phần tiêu đề “Cạm bẫy: auto không cho ra kiểu bạn nghĩ”
std::vector<bool> flags = {true, false};
auto& ref = flags[0]; // LỖI BIÊN DỊCH! Không thể lấy tham chiếu đến bit riêng lẻ
for (auto& b : flags) { // CŨNG có thể gây lỗi biên dịch tùy trình biên dịch!
b = true;
}

Nếu thực sự cần một container các giá trị bool với ngữ nghĩa “bình thường” (mỗi phần tử là một bool thật, có thể lấy tham chiếu), có vài lựa chọn:

#include <vector>
#include <deque>
std::deque<bool> flags1 = {true, false}; // deque KHÔNG có tối ưu hóa đặc biệt này
std::vector<char> flags2 = {1, 0, 1}; // Dùng char thay cho bool nếu cần tham chiếu thực sự
// Hoặc chấp nhận vector<bool> nhưng tránh dùng auto& / bool& với phần tử của nó
std::vector<bool> flags3 = {true, false};
bool value = flags3[0]; // OK - lấy GIÁ TRỊ (không phải tham chiếu)

std::vector<bool> không phải là một vector “bình thường” - nó nén dữ liệu ở cấp độ bit để tiết kiệm bộ nhớ, đánh đổi bằng việc không thể lấy tham chiếu thực sự đến từng phần tử.

  • flags[i] trả về một đối tượng proxy, không phải bool&
  • Nếu cần ngữ nghĩa container thông thường, cân nhắc dùng std::deque<bool> hoặc std::vector<char>