45

Why does this code work

std::vector<int> intVector(10);
for(auto& i : intVector)
    std::cout << i;

And this doesn't?

std::vector<bool> boolVector(10);
for(auto& i : boolVector)
    std::cout << i;

In the latter case, I get an error

error: invalid initialization of non-const reference of type ‘std::_Bit_reference&’ from an rvalue of type ‘std::_Bit_iterator::reference {aka std::_Bit_reference}’

for(auto& i : boolVector)
Community
  • 1
  • 1
Valentin
  • 1,058
  • 7
  • 16
  • 1
    Possible duplicate of [What is the correct way of using C++11's range-based for?](http://stackoverflow.com/q/15927033/3425536) (search for "vector") – emlai Dec 04 '15 at 01:38

3 Answers3

37

Because std::vector<bool> is not a container !

std::vector<T>'s iterators usually dereference to a T&, which you can bind to your own auto&.

std::vector<bool>, however, packs its bools together inside integers, so you need a proxy to do the bit-masking when accessing them. Thus, its iterators return a Proxy.
And since the returned Proxy is an prvalue (a temporary), it cannot bind to an lvalue reference such as auto&.

The solution : use auto&&, which will correctly collapse into an lvalue reference if given one, or bind and maintain the temporary alive if it's given a proxy.

Community
  • 1
  • 1
Quentin
  • 60,592
  • 7
  • 125
  • 183
9

std::vector<bool> does not obey the standard container rules.

In particular, its operator[] does not return bool&.

The loop in the invalid code

#include <vector>
#include <iostream>

int main() {
  std::vector<bool> boolVector(10);
  for (auto& i: boolVector)
      std::cout << i;
}

can be rewritten in any of three ways to iterate through the values:

  1. (read-only)

    for (auto i: boolVector)
        std::cout << i;
    
  2. (read-only, possibly inefficient)

    for (auto const& i: boolVector)  
        std::cout << i;
    
  3. (read/write)

    for (auto&& i: boolVector)
        std::cout << i;
    

The choice between the first and last is down to whether you need to modify the values in the vector, or just to read them.

Toby Speight
  • 25,191
  • 47
  • 61
  • 93
  • [`for(auto i :boolVector)` seems to be able to modify values](https://wandbox.org/permlink/qyxuyztzY2zSJV43). Is my example implementation-dependent? Also, could you please elaborate a little on why might `for(auto const& i : boolVector)` be inefficient? – Mike Apr 23 '18 at 16:31
  • 1
    Simply because it's an unnecessary level of indirection. Any decent optimizer will create the same code, so hence the "possibly". – Toby Speight Apr 23 '18 at 16:39
  • If I understand you correctly, this applies to any kind of reference, which means that `for(auto &&i : boolVector)` has the same issue, is that right? – Mike Apr 23 '18 at 16:41
  • Your link doesn't work for me (is it hidden in some JavaScript or cookie-based crap?), but yes, `auto i` will bind to the dereferenced type of `boolVector`'s iterator, i.e. `std::vector::reference`. Which likely modifies the underlying collection when assigned to. Vectors of `bool` are always surprising! – Toby Speight Apr 23 '18 at 16:43
6

vector<bool> is (usually) specialized explicitly to store each bool in a single bit, reducing the storage costs from one byte per value to one byte per eight values. No processor I know of offhand is bit addressable, so it's impossible to store a reference to the values in the vector<bool>. You need to use plain auto, not auto& for the iteration value i.

ShadowRanger
  • 124,179
  • 11
  • 158
  • 228