普通的右值引用和 std::forward 返回的有啥区别?

Posted

技术标签:

【中文标题】普通的右值引用和 std::forward 返回的有啥区别?【英文标题】:What's the difference between an ordinary rvalue reference and one returned by std::forward?普通的右值引用和 std::forward 返回的有什么区别? 【发布时间】:2018-08-24 06:04:16 【问题描述】:

我做不到:

int &&q = 7;
int &&r = q; 
//Error Message:
//cannot convert from 'int' to 'int &&'
//You cannot bind an lvalue to an rvalue reference

如果我理解正确,在初始化右值引用时,也会初始化一个临时变量。所以int &&q = 7;可以认为是:

int temp = 7;
int &&q = temp;

当在右侧使用参考时,我实际上是在使用裁判。所以int &&r = q;可以认为是:

int &&r = temp;  //bind an lvalue to an rvalue reference, cause error, understandable

以上是我对编译器错误发生的理解。


为什么添加std::forward可以解决这个问题?

int &&q = 7;
int &&r = std::forward<int>(q);

我知道std::forward 总是返回一个右值引用,std::forward 返回的引用与int&amp;&amp;q 有什么不同?

【问题讨论】:

等效不等于相同。硬编码值不被视为常规变量 不要认为我能比 Scott 更好地解释它,所以看看这个:isocpp.org/blog/2012/11/… 正如其他人所暗示的那样,int &amp;&amp;r = std::move(q); 在这种情况下会是更惯用的演员阵容。 std::forward 用于“完美转发”以传递具有模板类型的参数,例如 template &lt;typename T&gt; void f(T&amp;&amp; x)template &lt;typename... T&gt; void f(T&amp;&amp;... x) 另一个可能的惊喜:decltype(q) 等价于int&amp;&amp;decltype((q)) 等价于int&amp; 【参考方案1】:

std::forward 返回的引用与int&amp;&amp;q 有何不同?

他们的value categories 是不同的。请注意,类型和值类别是不同的东西。

q是一个命名变量,限定为lvalue,所以不能绑定右值引用。

(强调我的)

变量、函数、模板参数对象(C++20 起)或数据成员的名称,与类型无关,例如 std::cinstd::endl即使变量的类型是右值引用,由其名称组成的表达式也是左值表达式;

而从函数返回的右值引用被限定为xvalue,它属于rvalue。

函数调用或重载的运算符表达式,其返回类型为对对象的右值引用,如std::move(x)

【讨论】:

该死,这太难了。明白了一点,再次感谢元瑶。 @Rick 尝试始终分别考虑类型和值类别,它会变得更加清晰。【参考方案2】:

表达式qstd::forward&lt;int&gt;(q) 的区别在于前者是左值,而后者是右值(基本类别xvalue)。

我在this answer 中解决了类似的问题:关键是q 作为表达式是一个左值,因为它有一个名称。 std::forward&lt;int&gt;(q)(或等效的std::move(q))是没有名称的表达式,并且由于它们返回(未命名的)右值引用,它们是xvalues,它是右值的子类别,因此可以绑定到右值引用。

【讨论】:

以上是关于普通的右值引用和 std::forward 返回的有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

将 std::forward_as_tuple() 结果传递给可能从该对象的右值引用成员移动的多个函数?

std::forward vs std::move 同时将左值绑定到右值引用

第16课 右值引用_std::forward与完美转发

C++11 std::forward(配合&&右值引用)

C++11 std::forward(配合&&右值引用)

重新理解C11的右值引用