Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of rvalue reference to an array in C++11?

Arrays can not be returned by value from function (only by reference/constant reference) both in C++03 and C++11 (because we can not assign one array to another directly):

const size_t N = 10;
using Element = int;
using Array = Element[N];

Array array;

// does not compile
// Array GetArray()
// {
//     return array;
// }

Array& GetArrayRef()
{
    return array;
}

In C++ there was introduced a new reference type - rvalue reference. It can be used with arrays, too:

void TakeArray(Array&& value)
{
}

// ...

TakeArray(std::forward<Array>(array));
TakeArray(std::forward<Array>(GetArrayRef()));

What is the purpose of such reference (rvalue reference to an array)? Can it be used in any real code or is it only a defect of C++11 standard?

like image 774
Constructor Avatar asked Mar 21 '14 14:03

Constructor


1 Answers

An rvalue reference is a promise by either the programmer or compiler that the data being referred to is about to be disposed of1, and that the reader changing it in reasonable ways is acceptable if it makes reading faster (among other things).

If you are copying from an array of vector with the above promise, you can clearly move the contained vectors for a possibly massive performance boost.

So no, this is not a defect.

std::vector<int> data[1000];
void populate_data() {
  for( auto& v : data )
    v.resize(1000);
}
template<std::size_t N>
std::vector< std::vector<int> > get_data( std::vector<int>(&&arr)[N] ) {
  std::vector< std::vector<int> > retval;
  retval.reserve(N);
  for( auto& v : arr )
    retval.emplace_back( std::move(v) );
  return retval;
}
template<std::size_t N>
std::vector< std::vector<int> > get_data( std::vector<int>(const&arr)[N] ) {
  std::vector< std::vector<int> > retval;
  retval.reserve(N);
  for( auto const& v : arr )
    retval.emplace_back( v );
  return retval;
}
int main() {
  populate_data();
  auto d = get_data(data); // copies the sub-vectors
  auto d2 = get_data( std::move(data) ); // moves the sub-vectors
}

in this case, things are a bit contrived, but in general the source of the data could be moved from or not. The rvalue override does a move of the sub-data if the entire container is moved from (an rvalue reference), and otherwise does a copy.

With a bit more work, we could write the above so that it all happens in one method. So if we have an rvalue container (aka owning range), and the container itself isn't suitable for being moved from directly, we move the contents. But that is just advanced metaprogramming, and not essential to the answer.


1 The compiler does this in restricted situations involving the object being "impossible" to reference after the use, either because it is an anonymous temporary or because it is being used in certain circumstances in return values of functions. (It is not actually impossible, and in theory this C++11 feature could break pre-C++11 code, but it is pretty close). Programmers do this by an rvalue cast. What exactly it means that it will be "disposed of immediately" depends on context, but the general rule is that the consumer of the rvalue reference can put the object into any "valid" state that can still be destroyed and interacted with. The existence of rvalue-based swap means that putting it into a state that can only be destroyed is not safe.

like image 79
Yakk - Adam Nevraumont Avatar answered Sep 23 '22 20:09

Yakk - Adam Nevraumont