【问题标题】:move constructor and std::move confusion移动构造函数和 std::move 混淆
【发布时间】:2015-02-14 08:42:21
【问题描述】:

我正在阅读有关std::move、移动构造函数和移动赋值运算符的信息。 老实说,我现在得到的只是混乱。现在我有课了:

class A{
  public:
    int key;
    int value;
    A(){key = 3; value = 4;}
    //Simple move constructor
    A(A&& B){ A.key = std::move(B.key); 
              A.value = std::move(B.value);}
};
  1. 我认为B 是一个右值引用,为什么你可以将std::move 应用于一个ravlue 引用的成员?
  2. B.keyB.value 被移动后,两者都已失效,但 B 作为类 A 的对象如何失效?
  3. 如果我有A a(A())A() 显然是一个 rvlaue,A() 可以被std::move 移动吗?为什么?
  4. 同样,如果我有一个函数

    int add(int && z){ int x = std:move(z); int y = std:move(z); return x+y; }

如果我打电话给add(5)5 怎么会被移动,为什么? 还有注意z被移动了两次,z第一次被移动后就失效了,怎么再移动呢?

  1. 在定义foo (T && Z )TZ 可以是任何东西)时,在定义正文中为什么我应该使用std::move(Z),因为Z 已经通过右值引用传递,什么时候应该我用std::move?

【问题讨论】:

    标签: c++ c++11 move rvalue-reference


    【解决方案1】:

    您必须了解std::move 不会移动任何东西,而是将其参数“标记”为右值引用。从技术上讲,它将类型转换为右值引用。然后,右值引用被相应的移动构造函数或移动赋值运算符移动。对于仅包含具有普通移动 ctor/赋值运算符的成员的对象,移动 ctor/赋值运算符是普通的并且只是复制。一般情况下,对象的移动 ctor/赋值运算符会调用其所有成员的移动 ctor/赋值运算符。

    所以,无论何时写

    诠释 x = 10; int y = std::move(x);

    在赋值y = std::move(x) 的右侧,您有一个int&& 类型的右值引用。但是,int 没有重要的移动 ctor,右值只是简单地复制到 y 中,x 中没有任何变化。

    另一方面,

    string s = "一些字符串"; 字符串已移动_s = std::move(s); // 这里我们告诉编译器我们可以“窃取”s的资源

    不同。 moved_s 的移动构造函数启动,并“窃取”(即交换内部指针等)s 的资源,因为后者是右值引用。最后,s 将不包含任何元素。

    【讨论】:

    • 隐式生成的移动构造函数/赋值运算符将移动(而不是复制) - 如果我们谈论包含字符串成员变量的类,这种区别很重要,例如
    • 您能否详细说明第 5 项?例如,如果我有A b;,我可以使用A a(b);,为什么?
    • @user2345484 关于第 5 项,不,你不能,你需要使用move,只要你向你的函数传递除右值以外的任何东西。如果您尝试传递左值,它将无法编译。在此评论上方的示例中,您可以这样做,因为很可能A 具有将复制的默认复制ctor。要强制移动,请通过std::move(b)b 转换为右值引用,然后a 的移动ctor 将启动。
    • @user2345484 在我看来,std::move 这个名字很不幸,它应该被命名为 lvaluedecay_lvalue 或其他任何名称,而不是 move,因为它给人的印象是错误的该函数“执行”一些重要的动作。如果您查看std::moveen.cppreference.com/w/cpp/utility/move 的定义,您会发现它使用了包装函数std::remove_reference。看看后者以了解它是如何工作的。
    • 例如在第 4 项的功能中,我就是不明白为什么你应该这样做 std::move(z)A a(A()) 一样?由于z 已经作为右值引用传递,std::move(z) 也返回一个右值引用。
    【解决方案2】:
    1. B 是对象的名称。一旦一个引用被绑定,它就会命名一个对象。 “右值引用”、“左值引用”和“命名对象”的区别仅适用于在您走到这一步之前如何绑定名称。

    B.key 是作为此函数调用的参数提供的对象中的变量名称。

    1. “无效”不是移动标准术语的一部分。标准库对象被移出后仍处于未指定状态;但这不是这里发生的事情。

    A.key = std::move(B.key) 行调用 int 的内置赋值定义(这是一个简单的赋值,不是函数调用),这只是一个副本。所以B.key 保留了它的价值。

    1. 对于要编译的A(B())B 必须是您尚未定义的类型名。 (您的意思是A(A()) 吗?如果是,那么答案是“是”)。

    2. 见 2

    3. 当您想移出Z.foo 时使用std::move(Z.foo),而不是从Z.foo 复制。

    【讨论】:

      猜你喜欢
      • 2014-03-13
      • 2014-03-22
      • 1970-01-01
      • 2013-12-08
      • 2017-11-03
      • 1970-01-01
      • 2014-05-02
      • 1970-01-01
      • 2013-01-22
      相关资源
      最近更新 更多