我一直在研究C++ 11的一些新功能,而我注意到的一个功能是在声明变量(例如T&& var)时使用双“&”号。

首先,这只野兽叫什么?我希望Google允许我们搜索这样的标点符号。

这到底是什么意思?

乍一看,它似乎是一个双重引用(例如C风格的双重指针T** var),但是我很难考虑到这种情况的用例。

最佳答案

它声明了rvalue reference(标准提案文档)。

这是rvalue references的介绍。

这是对Microsoft标准库developers之一的右值引用的精彩深入研究。



C++ 03引用(在C++ 11中现在称为左值引用)之间的最大区别在于,它可以像临时元素一样绑定(bind)到右值,而不必使用const。因此,此语法现在合法:

T&& r = T();

右值引用主要提供以下内容:

移动语义。现在可以定义移动构造函数和移动赋值运算符,该运算符采用右值引用而不是通常的const-左值引用。移动的功能类似于副本,只是它没有义务保持源不变。实际上,它通常会修改源,使其不再拥有移动的资源。这对于消除多余的副本非常有用,尤其是在标准库实现中。

例如,复制构造函数可能如下所示:
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++ 11中,我们可以重载move构造函数:
foo(foo&& other)
{
   this->length = other.length;
   this->ptr = other.ptr;
   other.length = 0;
   other.ptr = nullptr;
}

注意这里的最大区别:move构造函数实际上是修改其参数。这将有效地将临时文件“移动”到正在构造的对象中,从而消除了不必要的复制。

move构造函数将用于临时文件和使用std::move函数(仅执行转换)显式转换为右值引用的非常量左值引用。以下代码都调用f1f2的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 <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
    return std::unique_ptr<T>(new T(a1));
}

如果我们调用factory<foo>(5),则将推导该参数为int&,即使foo的构造函数采用int,该参数也不会绑定(bind)到文字5。好吧,我们可以改用A1 const&,但是如果foo通过非const引用接受构造函数参数怎么办?为了实现真正的通用工厂功能,我们必须在A1&A1 const&上重载工厂。如果factory采用1个参数类型,则可能会很好,但是每个其他参数类型都会将必需的重载设置乘以2。这很快就无法维护。

右值引用通过允许标准库定义可以正确转发左值/右值引用的std::forward函数来解决此问题。有关std::forward的工作方式的更多信息,请参见this excellent answer

这使我们能够定义工厂功能,如下所示:
template <typename T, typename A1>
std::unique_ptr<T> factory(A1&& a1)
{
    return std::unique_ptr<T>(new T(std::forward<A1>(a1)));
}

现在,当传递给T的构造函数时,参数的rvalue/lvalue-ness得以保留。这意味着如果使用rvalue调用factory,则使用rvalue调用T的构造函数。如果使用左值调用factory,则使用左值调用T的构造函数。改进的工厂功能之所以有效,是因为以下一条特殊规则:



因此,我们可以像这样使用工厂:
auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1);   // calls foo(foo const&)

重要的右值引用属性:
  • 对于重载解析,左值倾向于绑定(bind)到左值引用,而右值倾向于绑定(bind)到右值引用。因此,为什么临时人员比复制构造函数/赋值运算符更喜欢调用移动构造函数/移动赋值运算符。
  • rvalue引用将隐式绑定(bind)到作为隐式转换的结果的rvalue和临时变量。即float f = 0f; int&& i = f;格式正确,因为float可以隐式转换为int;该引用将是转换后的结果。
  • 命名右值引用是左值。未命名的右值引用是右值。 这很重要,以了解为什么必须在以下位置进行std::move调用:foo&& r = foo(); foo f = std::move(r);
  • 关于c++ - C++ 11中的T &&(双“&”号)是什么意思?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/5481539/

    10-10 16:24