2

When I iterate over a vector<bool>, I find that the elements, dereferenced through the iterator, are recognized as if they were const. Why is that? Change either the container or the element type, such as list<bool> or vector<short>, and the elements are non-const. This code shows what I'm talking about:

typedef bool T;
#define C vector
istringstream &operator>>(istringstream &iss, T &v)
{
    cout << "non-const" << endl;
    return iss;
}

istringstream &operator>>(istringstream &iss, const T &v)
{
    cout << "const" << endl;
    return iss;
}

istringstream &operator>>(istringstream &iss, C<T> &c)
{
    for (C<T>::iterator it = c.begin(); it != c.end(); ++it)
    {
        iss >> *it;
    }
    return iss;
}

int main()
{
    C<T> c(1);
    istringstream iss("1");
    iss >> c;
}

For vector<bool>, this program prints "const" to the console. Change the typedef and manifest constant at the top to anything but those two combinations, and it prints "non-const." Also, if I replace the line, iss >> *it, with T v; iss >> v; *it = v;, it works as expected--all combinations print "non-const."

I see the same behavior with GCC 4.1.2 C++98 (via codepad.org) and VS2015 C++14+ (?).

plong
  • 1,723
  • 3
  • 14
  • 14

3 Answers3

6

Despite the name, vector<bool> contains no bools, and dereferencing its iterator doesn't give you a bool&. Instead, it gives you an object of type vector<bool>::reference, which tries to imitate the behavior of bool& as much as it can.

There's no way to convert a vector<bool>::reference to a bool&, so the non-const overload doesn't work. vector<bool>::reference can, however, be converted to a bool, which can then bind to the const bool&.

T.C.
  • 133,968
  • 17
  • 288
  • 421
5

To save memory, vector<bool> isn't an actual array of bool. A bool value need only a bit to be stored, but the smallest size possible is 1byte = 8bits. Therefore, by not using the trivial implementation, you can have an 8 times more efficient storage (as far as memory is concerned) then what a simple array of bool would provide.

However, the consequence is that an element of a vector<bool> is not a bool but a vector<bool>::reference which is almost but not quite the same as a bool&. Most operations are here, but some aren't (like |= for exemple)

Still you can either convert it to a bool, or initialize a new const bool object with it ... and do whatever you what with this new object

Amxx
  • 3,020
  • 2
  • 24
  • 45
2

std::vector<bool> is a specialization of std::vector that acts a space-efficent dynamic bitset. The iterators returned from begin() and end() actually point to an objects of a proxy class type representing booleans.

The references returned from dereferencing the iterators are prvalues of class type, not actual booleans. It is converted to a prvalue of bool type so that is why the overload taking a reference to const is perferred (rvalues can bind to references to const).

David G
  • 94,763
  • 41
  • 167
  • 253
  • Thanks, chris, T.C., and 0x499602D2. That explains it. Taking chris's advice, I found a [good article](http://www.gotw.ca/publications/mill09.htm). – plong Jun 21 '15 at 00:19