在C ++ 11中对rvalue引用数组的目的是什么?

时间:2014-03-21 14:56:45

标签: c++ arrays c++11 rvalue-reference

在C ++ 03和C ++ 11中,不能通过函数的值(仅通过引用/常量引用)返回数组(因为我们不能直接将一个数组分配给另一个数组):

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

在C ++中引入了一种新的引用类型 - 右值引用。它也可以用于数组:

void TakeArray(Array&& value)
{
}

// ...

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

此类引用的目的是什么(rvalue引用数组)?它可以用在任何实际代码中,还是仅仅是C ++ 11标准的缺陷?

4 个答案:

答案 0 :(得分:5)

rvalue引用是程序员或编译器承诺所引用的数据即将被处理掉 1 ,并且读者以合理的方式更改它是可以接受的阅读速度更快(除其他外)。

如果您使用上述承诺从vector数组中进行复制,则可以明确move包含vector s,以获得可能的巨大性能提升。

所以不,这不是缺陷。

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
}

在这种情况下,事情有点人为,但一般来说,数据来源可能是move d。如果整个容器是move d(右值参考),则rvalue覆盖会移动子数据,否则会复制。

通过更多的工作,我们可以编写上面的内容,以便一切都在一个方法中发生。因此,如果我们有一个rvalue容器(也就是拥有范围),并且容器本身不适合直接移动,我们move内容。但这只是高级元编程,而不是答案所必需的。


1 编译器在限制情况下执行此操作,涉及在使用后“无法”引用的对象,因为它是匿名临时的,或者因为它在某些情况下在返回值中使用功能。 (实际上这不是不可能的,理论上这个C ++ 11特性可能会破坏前C ++ 11代码,但它非常接近)。程序员通过右值转换来执行此操作。它究竟意味着它将“立即处置”取决于上下文,但一般规则是rvalue引用的使用者可以将对象置于任何仍然可以被销毁和交互的“有效”状态。基于rvalue的swap的存在意味着将其置于被销毁的状态是不安全的。

答案 1 :(得分:2)

r值参考是两件事:

  • 这是一个参考,所有这些都需要
  • 这是对编译器的承诺,即所引用的对象是未命名的(临时的)或可以被视为

在C ++中,通过引用传递数组允许,特别是确保它的长度,与C void func(int a[5])相比已经是一个改进,其中传递长度为2的数组是完全可以接受的(虽然好的编译器将会警告)。

对数组的r值引用的好处不那么直接,但是:

  • 一个未命名的对象不能别名;因此,r值引用必然没有别名,就像指定了restrict一样
  • 警告调用者可以移动数组的元素

因此,即使使用数组,也可以进行优化。

答案 2 :(得分:2)

你不能拥有阵列临时数据并不完全正确。你可以

void f(const std::string(&&x)[2]) {
   std::cout << "rvalue" << std::endl;
}

void f(const std::string(&x)[2]) {
   std::cout << "lvalue" << std::endl;
}

template<typename T>
using id = T;

int main() {
  f(id<std::string[]>{"Hello", "Folks"});
  f(id<std::string const[]>{"Hello", "Folks"});

  std::string array[] = { "Hello", "Folks" };
  std::string const arrayConst[] = { "Hello", "Folks" };
  f(array);
  f(arrayConst);
}

从C ++ 14开始,you are also allowed将支持的init列表直接传递给rvalue重载

f({ "Hello", "Folks" });

答案 3 :(得分:1)

它几乎不是缺陷,它引入了对象移动到目标的令人兴奋的能力。这意味着目标句柄被交换为即将超出范围的源。