静态转换为右值引用和 std::move 之间有啥区别吗

Posted

技术标签:

【中文标题】静态转换为右值引用和 std::move 之间有啥区别吗【英文标题】:is there any difference between static cast to rvalue reference and std::move静态转换为右值引用和 std::move 之间有什么区别吗 【发布时间】:2013-06-13 02:11:00 【问题描述】:

静态转换的描述说

如果 new_type 是右值引用类型,static_cast 将表达式的值转换为 xvalue。这种类型的 static_cast 用于在 std::move 中实现移动语义。(C++11 起)

这是否确认以下是等价的?

(A)

X x1;
X x2 = static_cast<X&&>(x1); 

(B)

X x1;
X x2 = std::move(x1);

【问题讨论】:

它们是等价的,但 move 不太容易出错。 【参考方案1】:

是的,有一个非常重要的区别:std::move 记录了您想要做什么。此外,演员阵容容易出现诸如忘记&amp; 或错误类型X 之类的错误。

可以看出,std::move 的输入更少。

【讨论】:

我只在movenot constexpr 并且我需要该属性时才使用“static_cast&lt;X&amp;&amp;&gt;”。【参考方案2】:

在 C++11 中,T&amp;&amp; 是一个右值引用。它们的行为类似于 C++ 98/03 中的左值引用。他们的目标——成为搬家的候选人。在 C++98 中,此构造可以出现在引用折叠中。

std::move - 将表达式转换为右值。它可以被称为 rvalue_cast,但不是。

原则上可以显式转换为T&amp;&amp; 类型。官方标准要花一些钱,但在ISO/IEC 14882:2011 草案中是这样的:

5.2.9 静态转换

8)

左值到右值 (4.1)、数组到指针 (4.2) 和函数到指针 (4.3) 转换应用于操作数......

从实际来看,使用std::move更方便。

考虑这个例子:

#include <stdio.h>
#include <utility>

class A

public:
A () printf ("A ()" "\n");
A (const A &) printf ("A (&)" "\n");
A (A &&) printf ("A (&&)" "\n");
A (const A &&) printf ("A (const &&)" "\n");
~ A () printf ("~ A ()" "\n");
;


int main ()

const A obj;
A obj2 (std::move (obj)); // 1-st approach
A obj3 (static_cast <const A&&> (obj));  // 2-nd approach

对我来说,第一种方法是:

更方便(你应该将static_cast转为const A&amp;&amp;,还是转为A&amp;&amp;?) 更明确(我可以在文本编辑器中使用搜索在项目中找到std::move) 不易出错。

【讨论】:

不需要rvalue_cast 关键字。如果您真的想使用该名称,请使用 template&lt;typename T&gt; template&lt;typename T&gt; constexpr auto rvalue_cast(T&amp;&amp; t) return std::move(t); ;【参考方案3】:

当 a 是右值时,您可以使用 static_cast&lt;A &amp;&amp;&gt;(a),但不应使用 std::move(a)。 当你使用A &amp;&amp; a = std::move(A()) 时,你会得到一个悬空引用。

基本思想是临时的生命周期不能通过“传递”来进一步延长:第二个引用,从临时绑定的引用初始化,不会影响它的生命周期。

std::move的实现有点像

template <typename T>
constexpr decltype(auto) move(T && __t) noexcept  // when used in std::move(A()),
                                                  // the lifetime of the temporary object is extended by __t

    return static_cast<typename std::remove_reference<T>::type &&>(__t);  // a xvalue returned, no lifetime extension


auto && a = std::move(A());  // the anonymous object wiil be destructed right after this line

【讨论】:

我看不出您在第二个示例中如何获得悬空引用。另外,当a 是右值时,std::move(a) 有什么问题? std::move((const int &amp;)a) 的结果就是 const int &amp;&amp;,这就是你想要的。 @SirGuy 在函数调用中与引用参数的临时绑定一直存在,直到包含该函数调用的完整表达式结束:如果函数返回的引用比完整表达式的寿命长,则它变成悬空参考。 move 将要转换的纯右值表达式的右值引用作为参数。 A&amp;&amp; a = std::move(A()); 将是一个悬空引用(同样的 static_cast 版本也是如此)...A a 不是引用,因此不是悬空引用 您的“更正错字”现在意味着您没有回答问题; std::movestatic_cast&lt;A&amp;&amp;&gt; 都在那里生成一个悬空引用。但问题是这两件事是否不同。您还在评论中说 static_cast&lt;A &amp;&amp;&gt;(A()) 与单个 A()" 相同,但是 std::move(A()) 也与演员表相同;并且两者都不与 A() 完全相同,因为它们是 xvalues 而不是 prvalues (您已经提到的相关的延长寿命问题) 难道我们没有一个新规则可以延长绑定到 rval 引用的临时生命吗? const ref 已经是这样了。【参考方案4】:

它们不是严格等价的。 std::move()的实现依赖于static_cast

template<typename _Tp>
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
 return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); 

它们的不同之处在于std::move()remove_reference 来处理reference collapse。 (A) 和 (B) 不严格等价的示例:

// Set up different behavior for lvalue and rvalue.
class T ;
void func(T&& t)  std::cout << "rvalue captured.\n"; 
void func(T& t)  std::cout << "lvalue captured.\n"; 
// Example:
Using X = T&;
X x1;
X x2 = static_cast<X&&>(x1); // (A) "lvalue captured."
X x3 = std::move(x1);        // (B) "rvalue captured."

【讨论】:

以上是关于静态转换为右值引用和 std::move 之间有啥区别吗的主要内容,如果未能解决你的问题,请参考以下文章

C++ std::move()函数(将一个左值强制转化为右值引用,继而可以通过右值引用使用该值)

C++ std::move()函数(将一个左值强制转化为右值引用,继而可以通过右值引用使用该值)

右值引用&&

使用 std::move 返回时“按右值引用返回”和“按值返回”之间的区别?

std::move()和std::forward()

error C2662 无法将左值绑定到右值 —— 变量永远是左值,即使它的类型为右值引用