47

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,108
  • 8
  • 18
  • 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") – Emil Laine Dec 04 '15 at 01:38

3 Answers3

39

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
  • 62,093
  • 7
  • 131
  • 191
10

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

In particular, its iterators' operator* do 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 const 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.


Notes:

  1. I say "possibly inefficient" because it has an unnecessary level of indirection. Any decent optimizer ought to create the same code as for the first sample.

  2. for (auto i: boolVector) also gives a read/write view on the vector (because we have made a copy of the proxy object). But I don't recommend that, because readers will likely expect such writes to have only local effect, as would happen with standard containers.

    And that's why I use auto const i for the read-only case; alternatively we could obtain a proxy-of-const using boolVector | std::views::as_const (since C++23) or by using a const reference to the vector (for (auto const& v = boolVector; auto i: v)).

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
  • @Mike, I've updated this answer to address your queries and to make the vector truly read-only in the first example. – Toby Speight Jul 29 '23 at 16:17
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
  • 143,180
  • 12
  • 188
  • 271