Range-for-loops and std::vector bool
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 bool
s 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.
Ranged based for loop for vector of booleans
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.
Range-for-loops and std::vector bool
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 bool
s 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.
how to complement the values of std::vector bool using range for loop taking element by reference
you can bind them (the returned proxy object) to rvalue reference
for (auto&& bit : rep)
bit = !bit;
godbolt
What's the difference between & and && in a range-based for loop?
7 years after I asked this question, I feel qualified to provide a more complete answer.
I'll start by saying that the code I chose back then is not ideal for the purpose of the question. That's because there is no difference between &
and &&
for the example.
Here's the thing: both
std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (auto& i : v)
{
std::cout << ++i << ' ';
}
std::cout << '\n';
and
std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (auto&& i : v)
{
std::cout << ++i << ' ';
}
std::cout << '\n';
are equivalent.
Here's proof:
#include <vector>
std::vector<int> v;
void f()
{
for (auto& i : v)
{
static_assert(std::is_same<decltype(i), int&>::value);
}
for (auto&& i : v)
{
static_assert(std::is_same<decltype(i), int&>::value);
}
}
But why?
Like David G said in the comments, a rvalue reference to a lvalue reference becomes a lvalue reference due to reference collapsing, eg
#include <type_traits>
using T1 = int&;
using T2 = T1&&;
static_assert(std::is_same<T1, T2>::value);
Note that this, however, is different:
for (int&& i : v)
{
// ...
}
and will fail, since a rvalue reference can't bind to a lvalue. Reference collapsing doesn't apply to this case, since there is no type deduction.
TLDR: for the standard containers, the difference between &
and &&
in a range-based for loop is:
value_type&
is validvalue_type&&
is not valid- Both
auto&
andauto&&
are equivalent tovalue_type&
Now let's try the opposite: an iterable object that returns rvalues.
#include <iostream>
struct Generated
{
int operator*() const
{
return i;
}
Generated& operator++()
{
++i;
return *this;
}
bool operator!=(const Generated& x) const
{
return i != x.i;
}
int i;
};
struct Generator
{
Generated begin() const { return { 0 }; }
Generated end() const { return { 6 }; }
};
int main()
{
Generator g;
for (const auto& i : g)
{
std::cout << /*++*/i << ' ';
}
std::cout << '\n';
for (auto&& i : g)
{
std::cout << ++i << ' ';
}
std::cout << '\n';
}
Here, auto&
doesn't work, since you can't bind a non-const lvalue to a rvalue.
Now we actually have const int&
and int&&
:
Generator g;
for (const auto& i : g)
{
static_assert(std::is_same<decltype(i), const int&>::value);
}
for (auto&& i : g)
{
static_assert(std::is_same<decltype(i), int&&>::value);
}
Range-based for loop with special case for the first item
Maybe a for_first_then_each
is what you're looking for? It takes your range in terms of iterators and applies the first function to the first element and the second function to the rest.
#include <iostream>
#include <vector>
template<typename BeginIt, typename EndIt, typename FirstFun, typename OthersFun>
void for_first_then_each(BeginIt begin, EndIt end, FirstFun firstFun, OthersFun othersFun) {
if(begin == end) return;
firstFun(*begin);
for(auto it = std::next(begin); it != end; ++it) {
othersFun(*it);
};
}
int main() {
std::vector<int> v = {0, 1, 2, 3};
for_first_then_each(v.begin(), v.end(),
[](auto first) { std::cout << first + 42 << '\n'; },
[](auto other) { std::cout << other - 42 << '\n'; }
);
// Outputs 42, -41, -40, -39
return 0;
}
Why does c++ for each loops accept r-values but std::ranges do not?
The for-loop works fine because the vector<int>
(rvalue) is valid while the loop is evaluated.
The second code snippet has 2 issues:
- The predicate must return a
bool
- the
vector<int>
you are using in the views object is dangling by the time it is evaluated, so the compiler does not accept it.
If instead of a vector<int>
rvalue, the object cannot dangle (lvalue) or holds references into something else that cannot not dangle, this would work.
For example:
auto vec = std::vector<int>({0, 1});
auto vw = vec | std::ranges::views::filter([](int i) { std::cout << i; return true; });
Or you can pass an rvalue that has references to a non-dangling vector<int>
object, like std::span
:
auto vec = std::vector<int>({0, 1});
auto vw = span{vec} | std::ranges::views::filter([](int i) { std::cout << i; return true; })
Here, std::span
is still a rvalue, but the compiler accepts it because the author of std::span
has created an exception for it.
For a custom type with references into something else (typically views), you can create your own exception by creating a specialization of the template variable enable_borrowed_range
.
In the case of vector it would look like this (but don't do this!):
template<> // don't do this
inline constexpr bool ranges::enable_borrowed_range<std::vector<int>> = true;
Your code now compiles but it will trigger undefined behavior because the vector is dangling once the view is evaluated.
Range-based for loop with auto specifier combined with static_cast
Not that this is a good idea as written, but this might be a useful example of a more general transform concept (and an evil lambda trick):
for(auto sv : v |
views::transform([](std::string_view x) {return x;})) …
Related Topics
What Is the Performance Penalty of C++11 Thread_Local Variables in Gcc 4.8
Why Does the Free() Function Not Return Memory to the Operating System
Why Is a NaïVe C++ Matrix Multiplication 100 Times Slower Than Blas
Why Would Connect() Give Eaddrnotavail
Force to Link Against Unused Shared Library
Colour Output of Program Run Under Bash
Realloc Without Freeing Old Memory
Memory Stability of a C++ Application in Linux
Error , Symbol 'Vector' Could Not Be Resolved
Hide or Crop Overlapping Text in Qlabel
How to Get Memory Information on Linux System
Using Input Function in C on Linux, Without Pressing Enter
What Does the Q_Object MACro Do? Why Do All Qt Objects Need This MACro