方法冗余移动调用的移动语义

时间:2018-07-20 14:51:55

标签: c++ c++11 move-semantics

说我有这堂课

struct Test {
  std::string a;
  void setA(std::string&& input) {
    a = input;
    input = "";
  }
}

在这里,我将input的内容移至a,然后将input置于安全的可破坏状态。这是移动语义的经典用法,可以避免复制。

现在说我有这堂课

struct Test {
  std::string a;
  void setA(std::string&& input) {
    DoSomeWork(input);
  }
  void DoSomeWork(std::string&& other) { /* ... */}
}

这仍然正确吗?还是应该使用DoSomeWork(std::move(input));?在这种情况下,我不知道是否需要搬家。


注意。 在情况1中,我收到一个右值引用作为输入,并且使用经典方法。

void setA(std::string&& input) {
  a = input;   //input is an rvalue referece and I "transfer" its content into a
  input = "";  //maybe useless but in some books (including c++ primer) I've seen that it's good practice to "reset" the moved-from object and leave it in a destructable state!

我明白。我不明白的是:

void setA(std::string&& input) {
  //recall that DoSomeWork accepts a std::string&&
  DoSomeWork(input);
}

如果我想将input传递给该函数并将其移动,我不知道是否需要std::move。我已经有一个右值参考,所以移动过程是自动的吗?还是需要std::move通话?

3 个答案:

答案 0 :(得分:3)

  

在这里,我将**" X mm rain expected in Y hours at your location"**的内容移到 for (var i = 0; i < myInputs.length; i++) { myInputs[i].addEventListener('blur', function (evt) { if(this.value!=this.defaultValue){ //value was changed now do your thing } }); myInputs[i].addEventListener('focus', function (evt) { evt.target.setAttribute("value",evt.target.value); }); }

不。您正在将input的内容复制到a

您可能会混淆类型和value categories。作为命名参数,input是一个左值;给定a,将调用复制分配运算符,而不是移动分配运算符。

  

然后我将input置于安全的可破坏状态。

这是多余的,应将工作留给a = input;的移动分配运算符。

是的,您应该使用std::moveinput转换为右值,例如

std::string

input

答案 1 :(得分:2)

在功能签名中

void setA(std::string&& input) { /* ... */ }

变量input绑定到一个右值,但本身是一个左值。当您要从其移动构造另一个对象时,需要事先将其强制转换为右值:

void setA(std::string&& input) {
    a = std::move(input);
}

请注意,现在无需现在将input设置为空字符串。您根本不应该再触摸此变量。当您将input传递给其他函数时,同样的道理也是如此:

void setA(std::string&& input) {
   /* Cast to rvalue necessary to preserve "rvalue-ness" of the argument: */
   DoSomeWork(std::move(input));
}

答案 2 :(得分:1)

  

在这里,我将输入内容移至a,然后将输入置于安全的可破坏状态。这是移动语义的经典用法,可以避免复制。

不,你不是。这是副本。除非您在命名的std::move上明确使用&&,否则它总是 副本。

此外,当您正确执行移动时,也不要将先前的对象置于“安全可破坏状态”。这是移动构造函数/赋值运算符的职责。

  

这仍然正确吗?

如果“正确”是指“执行动作”,否。同样,如果要从命名变量中移出,则必须 在其上使用std::move。这包括将其传递给右值参考参数。

唯一的例外是return <named_variable>;语句,即使这样,“ named_variable”也必须命名一个值,而不是引用(尽管C ++ 20可能允许以这种方式隐式移走右值引用变量)。