std::vector<bool> là trường hợp đặc biệt
Vấn đề
Phần tiêu đề “Vấn đề”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;}Vì sao lại đặc biệt?
Phần tiêu đề “Vì sao lại đặc biệt?”Để 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;}Giải pháp
Phần tiêu đề “Giải pháp”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)Tóm tắt
Phần tiêu đề “Tóm tắt”
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ảibool&- Nếu cần ngữ nghĩa container thông thường, cân nhắc dùng
std::deque<bool>hoặcstd::vector<char>