对象传递给std :: move但是没有从中移动?

ste*_*sic 23 c++ undefined-behavior rvalue-reference c++11

我正在审查一些像这样的代码,其中A是一个可移动的类型:

// Returns true exactly when ownership of a is taken
bool MaybeConsume(A&& a) {
  if (some condition) {
    Consume(std::move(a));  // ???
    return true;
  }
  return false;
}

// ... elsewhere ...

A a;
if (!MaybeConsume(std::move(a))) {
  a.DoSomething();  // !!!
}
Run Code Online (Sandbox Code Playgroud)

我们的静态分析工具抱怨a在移动(at !!!)后使用.IIUC std::move只是一个static_cast,并且在调用a移动构造函数或赋值运算符(可能是在Consume)之前,该对象实际上不会被删除.假设MaybeConsume在评论中满足合同,

  1. 这有用吗?
  2. 是UB吗?
  3. std::move???无操作?

(可能这个特殊的实例可以重构以避免微妙,但我仍然想要求我自己的理解).

Pra*_*ian 14

这是静态分析工具的虚假警告.

  1. 这有用吗?

是的,MaybeConsume正在做评论所说的.当它some condition为真时,它只接受其参数的所有权(假设Consume实际上确实从其参数移动了构造/赋值).

std::move确实只是一种幻想,static_cast<T&&>所以MaybeConsume(std::move(a))不转移所有权,你只是绑定对MaybeConsume参数的引用.

  1. 是UB吗?

不,你没有利用aif MaybeConsume表明它已经承担了其论证的所有权.

  1. std::move??? 没有操作?

嗯,这是一个无操作,因为它只是一个static_cast,但如果你想问这是否是不必要的,那么,不,它不是.在体内MaybeConsume,a是一个左值,因为它有一个名字.如果签名Consumevoid Consume(A&&),那么代码将不会编译std::move.


根据您显示的示例用法,似乎您不应该MaybeConsume使用prvalue参数调用,因为如果函数返回,调用者应该以某种其他方式使用该参数false.如果这是真的,那么你应该将其签名更改为bool MaybeConsume(A&).这可能会使您的静态分析工具感到高兴,因为这样可以让您编写if (!MaybeConsume(a)).

  • @stewbasic不完全,*有效但未指定*是C++标准库从`std`命名空间中的对象移动的承诺.对于您自己的类型来说,这是一个很好的指导方针,但是语言中没有任何内容可以强制执行.如果你选择这样做,你可以实现一种类型,如果以任何方式使用它会导致可怕的死亡.就标准库而言,只要您[不违反前提条件](/sf/answers/491982291/)进行该操作,您就可以以任何方式使用从对象移动. (4认同)