Is there a technical reason why std::exchange does not work on std::vector::reference or is it a bug in the implementation of GCC and Clang? With MSVC it compiles fine.
I have a setup like this (minimal example)
struct Manager
{
std::vector<bool> lifeTimes;
//Should return the state before trying to kill it
bool kill(std::size_t index)
{
return std::exchange(lifeTimes[index], false);
}
};
std::exchange would make this a really nice one liner but GCC complains about:
error: cannot bind non-const lvalue reference of type ‘std::_Bit_reference&’ to an rvalue of type ‘std::vector::reference’ {aka ‘std::_Bit_reference’}
So it seams it complains about the false
since only the second parameter is an rvalue
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);
}