Search code examples
c++c++11undefined-behaviorrvalue-reference

Object passed to std::move but not moved from?


I am reviewing some code like this, where A is a moveable type:

// 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();  // !!!
}

Our static analysis tool complains that a is used after being moved (at !!!). IIUC std::move is only a static_cast, and the object a won't actually get gutted until a move constructor or assignment operator is called (presumably in Consume). Assuming MaybeConsume satisfies the contract in the comment,

  1. Does this work?
  2. Is it UB?
  3. Is std::move at ??? a no-op?

(Probably this particular instance can be refactored to avoid the subtlety, but I would still like to ask for my own understanding).


Solution

  • It's a spurious warning from your static analysis tool.

    1. Does this work?

    Yes, MaybeConsume is doing what the comment says. It's only taking ownership of its argument when some condition is true (assuming Consume actually does move construct/assign from its argument).

    std::move is indeed just a fancy static_cast<T&&> so MaybeConsume(std::move(a)) is not transferring ownership, you're simply binding a reference to MaybeConsume's parameter.

    1. Is it UB?

    No, you're not making use of a if MaybeConsume indicates it has assumed ownership of its argument.

    1. Is std::move at ??? a no-op?

    Well, it's a no-op because it's just a static_cast, but if you meant to ask whether it's unnecessary, then, no, it isn't. Within the body of MaybeConsume, a is an lvalue because it has a name. If the signature of Consume is void Consume(A&&), then the code won't compile without that std::move.


    From the example usage you've shown, it seems you're not supposed to call MaybeConsume with a prvalue argument, since the caller should presumably use the argument in some other manner if the function returns false. If that's true, then you should change its signature to bool MaybeConsume(A&). This will probably make your static analysis tool happy because that would allow you to write if (!MaybeConsume(a)).