0
votes

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

1

1 Answers

2
votes

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);
  }