Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an authoritative way to guard against "use after move" mistakes in c++?

Tags:

c++

c++11

I find these mistake to be all-too-easy for developers to make. Is there a best practice or authoritative way to avoid this? Is there a compiler flag or hint that works across multiple platforms?

like image 596
Darkenor Avatar asked Mar 13 '19 13:03

Darkenor


People also ask

Should I rewrite my code to avoid moving-from error?

We recommend rewriting such code in a cleaner way. The check is path sensitive, so it will follow the flow of execution and avoid warning on code like the one below. In our analysis, we basically track what’s happening with the objects. If we reassign a moved-from object it is no longer moved from.

What is use after move in C++?

The use after move check is intended to find exactly such code, when we are using a moved-from object in a possibly unintended way. There are several interesting things happening in the above example: std::move does not actually move m. It’s only cast to a rvalue reference.

What is the use after move check?

The use after move check is intended to find exactly such code, when we are using a moved-from object in a possibly unintended way. There are several interesting things happening in the above example: std::move does not actually move m.

What is the use of @calling consume in move constructor?

Calling consume will move the internal representation of m. According to the standard, the move constructor must leave m in a valid state so it can be safely destroyed. However, we can’t rely on what that state is.


3 Answers

An effective rule of thumb: Never use std::move nor std::forward and never type cast to an rvalue (or universal) reference. If you never move from a variable or a reference, then you can't make the mistake of using it after. This approach obviously has a drawback, since those utilities are useful in some cases for turning a copy into a move when the move is sufficient; not to mention the cases where it is necessary.

Approach for your own types: Add assertions into member functions that verify whether the instance has been moved from, and rely on them to trigger during testing. The "moved" state will need to be stored as a member. Assertions and the member can be removed in release build. A drawback is that this adds otherwise unnecessary boilerplate to every member function.

An extra approach: Use a static analysis tool that attempts to detect the potential mistake.

A reasonable rule of thumb: Keep your functions short. When the function is short, a use will be close to the move, and thus the potential mistake is easier to spot.

like image 96
eerorika Avatar answered Oct 18 '22 22:10

eerorika


So the issue really is "read" after move. I think I agree that any use of std::move should be code reviewed as a potential risk. If the std::move is at the end of a function with a local value or value parameter, all good.

Anything else needs scrutiny, and any use of the variable after the move should be given attention. I guess giving the variable an "_movable" suffix will also help with the code review.

The few cases of write after move, such as swap will just have to be defended during the review.

Personally, I still treat std::move as a smell in the code, much like casts.

I'm not sure of any lint rules that would enforce this pattern, but I'm sure they are easy to write :-)

like image 2
Gem Taylor Avatar answered Oct 18 '22 22:10

Gem Taylor


To ban std::move is short-sighted. The best way to avoid read-after-move is to std::move at the end of the scope that introduced the moved-from variables.

like image 2
j6t Avatar answered Oct 18 '22 23:10

j6t