gccvisual-c++stlclangc++14

std::vector<bool>::reference with std::exchange


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


Solution

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