我一直在研究C++11的一些新特性,我注意到其中一个是在声明变量时使用了双符号,如T&&;var
首先,这个野兽叫什么?我希望谷歌能允许我们搜索这样的标点符号
它到底是什么意思?
乍一看,它似乎是一个双重引用(就像C风格的双指针T**var),但我很难想到这个用例
它声明了一个右值引用(标准建议书文档)
下面介绍右值引用
下面是微软标准库开发人员之一对右值引用的精彩深入介绍
注意:有关MSDN的链接文章(“VC10中的右值引用:C++0x功能,第2部分”)非常清楚地介绍了右值引用,但对右值引用做了一些陈述,这些陈述在C++11标准草案中曾经是正确的,但在最后一个标准中却不是正确的!具体地说,它在不同的点上表示右值引用可以绑定到左值,这曾经是正确的,但是已经改变了(例如int x;int&;rrx=x;不再在GCC中编译)-drewbarbs 7月13日14日16:12
C++03引用(现在在C++11中称为左值引用)之间的最大区别在于,它可以像临时值一样绑定到右值,而不必是常量。因此,此语法现在是合法的:
T&&;r=T();
右值参考主要提供以下内容:
移动语义。现在可以定义一个移动构造函数和移动赋值运算符,它采用右值引用而不是通常的常量左值引用。移动的功能类似于复制,除非它没有义务保持源不变;事实上,它通常会修改源,使其不再拥有移动的资源。这对于消除无关拷贝非常有用,尤其是在标准库实现中
例如,复制构造函数可能如下所示:
foo(foo const&;other)
{
此->;长度=其他长度;
此->;ptr=新整数[其他长度];
副本(other.ptr,other.ptr+other.length,this->;ptr);
}
如果这个构造函数被传递了一个临时的,那么这个副本就没有必要了,因为我们知道临时的将被销毁;为什么不利用已经分配的临时资源呢?在C++03中,无法阻止复制,因为我们无法确定是否传递了临时副本。在C++11中,我们可以重载移动构造函数:
foo(foo&;其他)
{
此->;长度=其他长度;
此->;ptr=other.ptr;
其他长度=0;
other.ptr=nullptr;
}
注意这里的巨大区别:move构造函数实际上修改了它的参数。这将有效地;“移动”;将临时副本导入正在构造的对象中,从而消除不必要的副本
move构造函数将用于临时变量和使用std::move函数显式转换为右值引用的非常量左值引用(它只执行转换)。以下代码都调用了f1和f2的移动构造函数:
foo f1((foo());//将临时文件移动到f1中;临时变为“临时”;“空的”;
foo f2=std::move(f1);//将f1移入f2;f1现在是;“空的”;
完美转发。右值引用允许我们正确地转发模板函数的参数。以该工厂功能为例:
模板<;类型名称T、类型名称A1>;
标准::唯一性\u ptr<;T>;工厂(A1和A1)
{
返回标准::唯一的_ptr<;T>;(新的T(a1));
}
如果我们调用工厂<;foo>;(5) ,参数将被推断为int&,即使foo的构造函数采用int,它也不会绑定到文本5。那么,我们可以使用A1常量&,但是如果foo通过非常量引用获取构造函数参数会怎么样?要制作一个真正通用的工厂函数,我们必须在A1&和onA1常数&。如果factory采用1个参数类型,这可能很好,但每个额外的参数类型都会将必要的重载集乘以2。这很快就无法维持了
右值引用通过允许标准库定义可以正确转发左值/右值引用的std::forward函数来解决此问题。有关std::forward工作原理的更多信息,请参阅此优秀答案
这使我们能够像这样定义工厂功能:
模板<;类型名称T、类型名称A1>;
标准::唯一性\u ptr<;T>;工厂(A1&;A1)
{
返回std::unique_ptr<;T>;(新T(std::forward<;A1>;(A1));
}
现在,当传递给T的构造函数时,参数的右值/左值性被保留。这意味着,如果使用右值调用factory,则使用右值调用T的构造函数。如果使用左值调用工厂,则使用左值调用T的构造函数。改进的factory功能之所以有效,是因为有一条特殊规则:
当函数参数类型为
表格T&&其中T是一个模板
参数和函数参数
是类型A的左值,类型A&是
用于模板参数推断
因此,我们可以像这样使用工厂:
自动p1=工厂<;foo>;(foo());//呼叫foo(foo&;)
自动p2=工厂<;foo>;(*p1);//调用foo(foo const&;)
重要的右值参考属性:
- 对于重载解析,左值比左值引用更喜欢绑定,右值比右值引用更喜欢绑定。因此,为什么临时人员更喜欢调用移动构造函数/移动赋值操作符而不是复制构造函数/赋值操作符
- 右值引用将隐式绑定到右值和隐式转换结果的临时值。i、 e.
float f=0f;国际及&;i=f格式良好,因为float可以隐式转换为int;该引用将是转换结果的临时引用 - 命名的右值引用是左值。未命名的右值引用是右值。这对于理解为什么在:
foo&&;r=foo();foo f=std::move(r)