Why Vector≪Bool≫::Reference Doesn't Return Reference to Bool

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 bools, it can't return a reference to a bool from the element access methods. It doesn't have any bools 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 bools 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



Leave a reply



Submit