Why vector<bool>::reference doesn't return reference to bool?
Vector is specialized for bool.
It is considered a mistake of the std. Use vector<char>
instead:
template<typename t>
struct foo {
using type = t;
};
template<>
struct foo<bool> {
using type = char;
};
template<typename t, typename... p>
using fixed_vector = std::vector<typename foo<t>::type, p...>;
Occasionally you may need references to a bool contained inside the vector. Unfortunately, using vector<char>
can only give you references to chars. If you really need bool&
, check out the Boost Containers library. It has an unspecialized version of vector<bool>
.
Getting a bool reference from std::vector<bool>
Is there a way how to get a reference of boolean variable from std::Vector?
No.
Or any different solution?
Return typename std::vector<T>::reference
instead of T&
. For bool
, it will return the vector's proxy type; for others, it will return a regular reference.
Or specialise A<bool>
to use something other than vector<bool>
.
Or use some other type (perhaps char
, or a simple class wrapping a bool
) instead of bool
.
Trying to change a value in a vector bool
std_lib_facilities.h defines the following helper template to add a bit of error checking helpful to folks beginning their programming careers:
// trivially range-checked vector (no iterator checking):
template< class T> struct Vector : public std::vector<T> {
using size_type = typename std::vector<T>::size_type;
/* #ifdef _MSC_VER
// microsoft doesn't yet support C++11 inheriting constructors
Vector() { }
explicit Vector(size_type n) :std::vector<T>(n) {}
Vector(size_type n, const T& v) :std::vector<T>(n, v) {}
template <class I>
Vector(I first, I last) : std::vector<T>(first, last) {}
Vector(initializer_list<T> list) : std::vector<T>(list) {}
*/
using std::vector<T>::vector; // inheriting constructor
T& operator[](unsigned int i) // rather than return at(i);
{
if (i<0 || this->size() <= i) throw Range_error(i);
return std::vector<T>::operator[](i);
}
const T& operator[](unsigned int i) const
{
if (i<0 || this->size() <= i) throw Range_error(i);
return std::vector<T>::operator[](i);
}
};
T& operator[](unsigned int i)
and its const
variant return a reference to T
and in this case T
is bool
. To get the element it calls through to std::vector
's operator[]
to do the grunt work.
Unfortunately std::vector<bool>
is not a proper library container. Because it packs bits rather than storing bool
s, it can't return a reference to a bool
from the element access methods. It doesn't have any bool
s to reference. Instead it returns a groovy reference
class that conceals the bit packing from sight and usually makes life a bit easier. Under most circumstances the element access methods will behave exactly the same way as a regular vector
.
But not this time. It's not returning a reference and it's not returning a reference to a bool
so the simple pass-through fails to compile.
Common work arounds are to use a std::vector
containing some small integer type or an enum
or use a std::deque<bool>
which can be used largely the same way and doesn't have a bit-backing specialization. If the size is known at compile time, consider using a std::bitset
.
Why doesn't std::swap work on vector<bool> elements under Clang/Win?
The standard doesn't require this to compile on any toolchain!
First recall that vector<bool>
is weird and subscripting it gives you a temporary object of a proxy type called std::vector<bool>::reference
, rather than an actual bool&
.
The error message is telling you that it cannot bind this temporary to a non-const
lvalue reference in the generic template <typename T> std::swap(T& lhs, T& rhs)
implementation.
Extensions!
However, it turns out that libstdc++ defines an overload for std::swap(std::vector<bool>::reference, std::vector<bool>::reference)
, but this is an extension to the standard (or, if it is in there, I can't find any evidence for it).
libc++ does this too.
I'd guess that the Visual Studio stdlib implementation, which you're still using, doesn't, but then to add insult to injury you can bind temporaries to lvalue references in VS (unless you're using conformance mode), so the standard, "generic", std::swap
function works until you substitute the VS compiler for the stricter Clang compiler.
As a result, you've been relying on extensions on all of the three toolchains for which it did work for you, and the Clang on Windows combination is the only one actually exhibiting strict compliance.
(In my opinion, those three toolchains should have diagnosed this so you didn't ship non-portable code this whole time. )
What now?
It may be tempting to add your own specialisation of std::swap
and std::vector<bool>::reference
, but you're not allowed to do this for standard types; indeed, it would conflict with the overloads that libstdc++ and libc++ have chosen to add as extensions.
So, to be portable and compliant, you should change your code.
Perhaps a good old-fashioned:
const bool temp = vb[0];
vb[0] = vb[1];
vb[1] = temp;
Or make use of the special static member function that does exactly what you wanted:
std::vector<bool>::swap(vb[0], vb[1]);
Also spellable as follows:
vb.swap(vb[0], vb[1]);
std::vector<bool>::reference with std::exchange
It is not a bug, MSVC compiles your code because it has an extension which enables binding temporary object (Rvalue) to non-const Lvalue reference.
Below code compiles with MSVC:
void foo(int& i) {}
foo(20); // you are passing Rvalue and it is bound to Lvalue reference
Above code doesn't compile under G++ or CLang, when you add const
to make reference to
const Lvalue, it works:
void foo(const int&){}
foo(20); // you can bind Rvalue to const Lvalue reference
A few words about vector. operator[]
for vector<T>
where T
is every type except bool returns T&
:
T& vector<T>::operator[](index) // where T is not bool
For bool
vector class template has specialization. Values of bool
are stored to hold one bit space, because you cannot use address-of operator for one bit, vector<bool>::operator[](index)
cannot return reference. vector<bool>
has inner proxy class which manipulates bits (call this class as reference).
vector<bool>::reference vector<bool>::operator[](index)
^^^^^^^^^
as you see object of proxy is passed by value.
So when you call
return std::exchange(lifeTimes[index], false);
you are passing temporary objecy (Rvalue) to exchange
which takes first argument by reference to non-const Lvalue. This is the cause that G++ discards this code. If you want to compile it you can explicitly create Lvalue object of proxy class and pass it:
bool kill(std::size_t index)
{
std::vector<bool>::reference proxyForBit = lifeTimes[index];
return std::exchange(proxyForBit, false);
}
Why does std::foreach not work with a std::vector<bool>?
Reason
The problem stems from the fact that dereferencing an iterator that came from std::vector<bool>
doesn't return bool&
, but rather a proxy object. Thus, it is not regarded as stl container (thanks to @KillzoneKid).
Fix
Use auto element
in the parameter list. In general, if you don't care about the type, use auto&&
in the lambda parameter list.
#include <vector>
#include <iostream>
#include <algorithm>
int main () {
std::vector<bool> list {true, true, false};
auto reset = [](auto && element){element = false;};
auto print = [](int element) {std::cout<< element << " ";};
std::for_each(list.begin(), list.end(),reset);
std::for_each(list.begin(), list.end(),print);
}
Demo.
Trying to use auto&
will trigger compilation error again, as the proxy returned is not lvalue, but rvalue. Thus, auto&&
has even more benefits than usual.
vector<bool>::operator[] misbehavior?
vector<bool>::operator[]
neither yields a bool
nor a reference to a bool
. It just returns a little proxy object that acts like a reference. This is because there are no references to single bits and vector<bool>
actually stores the bool
s in a compressed way. So by using auto
you just created a copy of that reference-like object. The problem is that C++ does not know that this object acts as a reference. You have to force the "decay to a value" here by replacing auto
with T
.
Why isn't vector<bool> a STL container?
For space-optimization reasons, the C++ standard (as far back as C++98) explicitly calls out vector<bool>
as a special standard container where each bool uses only one bit of space rather than one byte as a normal bool would (implementing a kind of "dynamic bitset"). In exchange for this optimization it doesn't offer all the capabilities and interface of a normal standard container.
In this case, since you can't take the address of a bit within a byte, things such as operator[]
can't return a bool&
but instead return a proxy object that allows to manipulate the particular bit in question. Since this proxy object is not a bool&
, you can't assign its address to a bool*
like you could with the result of such an operator call on a "normal" container. In turn this means that bool *pb =&v[0];
isn't valid code.
On the other hand deque
doesn't have any such specialization called out so each bool takes a byte and you can take the address of the value return from operator[]
.
Finally note that the MS standard library implementation is (arguably) suboptimal in that it uses a small chunk size for deques, which means that using deque as a substitute isn't always the right answer.
Could std::vector<bool>::iterator implement its deref operator to return a reference to _Bit_reference?
vector<bool>
does not contain Bit_reference
or any other such types. And vector<bool>
is required to be a multipass range. This means that whatever operator*
returns, it can't be a reference to something stored in the iterator, since you can increment the iterator and still access the reference
the previous value of the iterator returned:
auto &&ref = *it;
++it;
//`ref` still references the previous bit.
This is where your proposed code fails.
So if operator*
is to return a reference to an object... where is the object being defined? It can't be in the vector<bool>
, and it can't be in the iterator. And it can't be defined within operator*
, because then you'd be returning a reference to a local that will be destroyed once the function returns.
So where is it?
It has to be a temporary, an object created within operator*
itself and returned by value.
error: invalid initialization of non-const reference of type ‘bool&’ from an rvalue of type ‘std::vector<bool>::reference {aka std::_Bit_reference}’
That's because std::vector< bool >
is not what it looks like.
There's a specialization for std::vector
with type bool
- it's space optimized and uses a single bit for each element.
You can try to use uint8_t
or something like this, if you need this functionality. Or just return bool
, not bool&
.
The reference, returned by operator[]
is not a standard reference, but a proxy class, which complicates the things here.
There are a lot of similar questions about this here:
- operator |= on std::vector<bool>
- Why vector<bool>::reference doesn't return reference to bool?
- Is the use of std::vector<bool> objects in C++ acceptable, or should I use an alternative?
And others. Read more about std::vector< bool >
specialization.
Related Topics
Boolean Expression (Grammar) Parser in C++
Displaying the #Include Hierarchy For a C++ File in Visual Studio
Elegant Solution to Duplicate, Const and Non-Const, Getters
Lock-Free Progress Guarantees in a Circular Buffer Queue
Floating Point VS Integer Calculations on Modern Hardware
How to Pass Parameters Correctly
Calculate the Factorial of an Arbitrarily Large Number, Showing All the Digits
What's the Difference Between a Header File and a Library
C++ Standard: Dereferencing Null Pointer to Get a Reference
How Does C++ Linking Work in Practice
Correct Way of Declaring Pointer Variables in C/C++
What Are Inline Namespaces For
Pre-2016 Valgrind: Memory Still Reachable With Trivial Program Using ≪Iostream≫
How Is Std::Function Implemented
Difference Between Angle Bracket ≪ ≫ and Double Quotes " " While Including Header Files in C++
Are "Anonymous Structs" Standard? And, Really, What *Are* They