这里是rvalue references的介绍。
这里有一个奇妙的深入了解微软的标准库developers之一的值引用。(但阅读本文之前,请参阅本回答之后的注释中的Caution)。
C 03引用(现在在C 0x中称为左值引用)的最大区别是它可以像临时的那样绑定到右值,而不必是const。因此,这种语法现在是合法的:
T&& r = T();
右值引用主要提供以下内容:
移动语义。现在可以定义一个移动构造函数和移动赋值运算符,它使用一个右值引用而不是通常的常量值引用。一个移动的功能像一个副本,除了它没有义务保持源不变;实际上,它通常修改源,使其不再拥有移动的资源。这是伟大的消除无关的副本,特别是在标准库实现。
例如,复制构造函数可能如下所示:
foo(foo const& other)
{
this->length = other.length;
this->ptr = new int[other.length];
copy(other.ptr, other.ptr + other.length, this->ptr);
}
如果这个构造函数被传递一个临时的,该副本将是不必要的,因为我们知道临时将被销毁;为什么不使用已经分配的临时资源?在C 03中,没有办法防止复制,因为我们不能确定我们是否通过了一个临时。在C 0x中,我们可以重载一个移动构造函数:
foo(foo&& other)
{
this->length = other.length;
this->ptr = other.ptr;
other.length = 0;
other.ptr = nullptr;
}
注意这里的巨大差异:move constructor实际上修改它的参数。这将有效地将临时“移动”到正在构造的对象中,从而消除不必要的副本。
move构造函数将用于临时和非const常量引用,它们使用std :: move函数(仅执行转换)显式转换为右值引用。以下代码都调用f1和f2的move构造函数:
foo f1((foo())); // Move a temporary into f1; temporary becomes "empty"
foo f2 = std::move(f1); // Move f1 into f2; f1 is now "empty"
完美的转发。右值引用允许我们正确地转发模板函数的参数。以这个工厂函数为例:
template
std::unique_ptr factory(A1& a1)
{
return std::unique_ptr(new T(a1));
}
如果我们调用factory(5),则参数将被推导为int& amp,这将不会绑定到文字5,即使foo的构造函数接受int。好吧,我们可以改用A1 const&,但如果foo接受构造函数参数的非const引用?为了使一个真正的通用工厂功能,我们将不得不超载工厂在A1&和A1 const&如果工厂需要1个参数类型,那么这可能很好,但是每个附加的参数类型都会将必要的重载设置乘以2.这是非常快速的不可维护的。
右值引用通过允许标准库定义一个可以正确转发lvalue / rvalue引用的std :: forward函数来解决这个问题。有关std :: forward的工作原理的更多信息,请参阅this excellent answer。
这使我们可以这样定义工厂函数:
template
std::unique_ptr factory(A1&& a1)
{
return std::unique_ptr(new T(std::forward(a1)));
}
现在,当传递给T的构造函数时,参数的rvalue / lvalue-ness被保留。这意味着如果工厂用一个右值调用,T的构造函数被调用一个右值。如果工厂以左值调用,则T的构造函数使用左值调用。改进的工厂功能的工作原理是一个特殊的规则:
When the function parameter type is of
the form T&& where T is a template
parameter, and the function argument
is an lvalue of type A, the type A& is
used for template argument deduction.
因此,我们可以使用工厂这样:
auto p1 = factory(foo()); // calls foo(foo&&)
auto p2 = factory(*p1); // calls foo(foo const&)
重要的右值引用属性:
>对于重载解析,l值偏好绑定到lvalue引用,rvalues偏好绑定到rvalue引用。因此,为什么临时优选在复制构造函数/赋值运算符上调用移动构造函数/移动赋值运算符。 >右值引用将隐式绑定到右值和作为隐式转换结果的临时值。即float f = 0f;内部&& i = f;是良好形成的,因为float是隐式可转换为int;引用将是转换的结果的临时。 >命名的右值引用是左值。未命名的右值引用是右值。这是很重要的理解为什么std :: move调用是必要的:foo&& r = foo(); foo f = std :: move(r);