Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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).

like image 278
stewbasic Avatar asked Feb 15 '18 04:02

stewbasic


People also ask

What happens to object after std :: move?

std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object. In particular, std::move produces an xvalue expression that identifies its argument t . It is exactly equivalent to a static_cast to an rvalue reference type.

Does std :: move make a copy?

std::move is actually just a request to move and if the type of the object has not a move constructor/assign-operator defined or generated the move operation will fall back to a copy.

Is std :: move required?

std::move itself does "nothing" - it has zero side effects. It just signals to the compiler that the programmer doesn't care what happens to that object any more. i.e. it gives permission to other parts of the software to move from the object, but it doesn't require that it be moved.

What does move () do in C ++?

std::move in C++Moves the elements in the range [first,last] into the range beginning at result. The value of the elements in the [first,last] is transferred to the elements pointed by result. After the call, the elements in the range [first,last] are left in an unspecified but valid state.


1 Answers

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)).

like image 160
Praetorian Avatar answered Nov 15 '22 15:11

Praetorian