Skip to content

26 Beginner The Hundred

The Container of Lies

the copy that kept in touch

#include <iostream>
#include <vector>

int main() {
    std::vector<int> nums = {0, 0, 0};
    std::vector<bool> flags = {false, false, false};

    auto n = nums[0];   // copy the first number
    n = 42;             // change the copy

    auto f = flags[0];   // copy the first flag
    f = true;            // change the copy

    std::cout << std::boolalpha;
    std::cout << "n = " << n << ", nums[0]  = " << nums[0] << '\n';
    std::cout << "f = " << f << ", flags[0] = " << flags[0] << '\n';
}

Run it. What does it print?

Answer
n = 42, nums[0]  = 0
f = true, flags[0] = true

The int copy behaved. The bool "copy" flipped a bit inside the vector.

Why

std::vector<bool> is a mandated specialization that packs eight elements into each byte — and you cannot form a bool& to a single bit. So its operator[] returns a small proxy object, std::vector<bool>::reference, that remembers which bit it stands for. auto dutifully deduces that proxy type, so f is no bool: assigning to it calls the proxy's operator=, which writes straight through to the vector. n really is a detached int, because nums[0] returns int& and auto drops references. The proxy is also why you can't point into the vector — build with g++ -DSHOW_BUG ... and the hidden line bool* p = &flags[0]; fails with error: cannot convert ‘std::vector<bool>::reference*’ to ‘bool*’. No warning flag catches the write-through; the code is perfectly legal.

The fix

Name the type and you get a genuine copy:

bool f = flags[0];   // a real bool, detached from the vector

When you need honest, addressable booleans, use std::vector<char> or std::deque<bool> — neither one is specialized.

Takeaway: std::vector<bool>[i] hands you a write-through proxy, not a bool& — spell out bool when you want a copy.

Try it: g++ -std=c++17 main.cpp -o demo && ./demo

Open in Compiler Explorer ↗ Open SHOW_BUG variant ↗ Quiz this entry