4

std::vector<bool> uses proxy iterators.

So the following code will not compile (code taken from the accepted answer in related question ):

vector<bool> v = {true, false, false, true};
for (auto& x : v)
    x = !x;

In the related question, the accepted answer states that to modify the components of the vector in place, we must use

for (auto&& x : v)
    x = !x;

But if I simply do:

for (auto x : v)
    x = !x;

This produces identical results. So is the && not needed?

Further why does the following 2 codes not modify the components?

for (bool &&x : v)
    x = !x;

and

for (bool x : v)
    x = !x;
24n8
  • 1,898
  • 1
  • 12
  • 25
  • Inspecting the *type* of `x` with the second and third snippets, as compared to the final two, is probably going to be illuminating. – WhozCraig Jan 16 '20 at 17:06
  • Put a breakpoint inside the `for` loop and you will see that using `auto` does not result in `x` having a `bool` type. – Romen Jan 16 '20 at 17:06
  • Guys, read the first line of the question. – Max Langhof Jan 16 '20 at 17:09
  • I think I misunderstood how proxy iterators work. That answers my last question. I'm still confused on why `auto &&` and `auto` generates the same behavior? In all the related questions I've seen, the answers use `auto &&`. – 24n8 Jan 16 '20 at 17:11
  • 2
    The whole point of auto&& is that it works both for vector and vector. You could also not use auto at all and be explicit about the type. – Marc Glisse Jan 16 '20 at 17:12
  • @MarcGlisse just `auto` works also for both int and bool. – n314159 Jan 16 '20 at 17:17
  • @n314159 but will just `auto` allow you to modify the values of the vector in your loop? Yes for bool, no for int. I believe that's why Marc is saying use `auto&&` whenever you want to change things. It's just one less thing to worry about. – scohe001 Jan 16 '20 at 17:20
  • Ah, yes, sry I missed that. – n314159 Jan 16 '20 at 17:30

1 Answers1

5

TL;DR: The proxy object knows how to read and write the single bits, regardless of how you keep it. Converting the proxy object to bool loses that information.


for (auto&& x : v)
    x = !x;

and

for (auto x : v)
    x = !x;

have the same behavior because in each case the proxy object (std::vector<bool>::reference) obtained from dereferencing a std::vector<bool>::iterator is stored in x. Whether the proxy object is stored by value or reference doesn't matter - its behavior of modifying the proxied bit is the same.

In

for (bool &&x : v)
    x = !x;

and

for (bool x : v)
    x = !x;

the proxy object is implicitly converted to a bool. This necessarily loses the information needed (and thus the capability) to affect the compressed bit.

Note that these are all implementation-defined. Your implementation is allowed to forego the space optimization too, in which case the behavior you see could be different. Only auto&& works in every case.

Max Langhof
  • 23,383
  • 5
  • 39
  • 72
  • I would add that the reason for using `auto&&` over `auto` is that it is more generic. If you replaced the `vector` with a vector of heavy objects, `auto` would make copies, while `auto&&` would not. (This is also the case for the current code, as you said, but here it doesn't matter performancewise) – n314159 Jan 16 '20 at 17:15
  • 1
    @n314159 I did not add that because I would strongly advise against combining `std::vector` and generic code. Well, generic in the `std::vector` sense. It's just a giant minefield. – Max Langhof Jan 16 '20 at 17:16
  • I agree, that one should not do that (personally, I would just stay far away from `std::vector` if possible). I just think OP wonders, why nearly all code in this context uses `auto&&` instead of `auto`. That's why I find it somewhat relevant. – n314159 Jan 16 '20 at 17:33