C++ – Documenting the Effect of std::move

cc++11comments

As my team move towards embracing the new features in C++11, I'm struggling with how to make the side-effects of std::move self-documenting in the code.

The issue is that an object that has been moved should no longer be used after that point. All I can come up with is a comment:

std::string x = Func();
Other(x);   // x is zombied.

Obviously this is error-prone and will not handle the cases where deeper code might unwittingly do a std::move.

Has anyone else dealt with this issue?

I have a feeling that diagnosing the use of zombied objects could be as much a pain as tracking down leaks or heap corruption.

Best Answer

The r-value reference only binds to rvalues. In the above code, Other(std::string &&) will not be considered. So only problem might be if the function std::string & but moved it internally.

The only way to get r-value out of l-value is a std::move. So as long as you take care not to use std::move on function argument you received by l-value reference, there will be no surprises.

Moving something you received by l-value reference should be considered a bug. Unfortunately the rules of the language itself are unable to catch it, so it's a job for a static checker. I don't know whether any of them does already though.

Of course a function taking a non-const l-value reference could always screw up the passed object and the move just adds another way to do it, but the problem already existed.

I would generally recommend avoiding functions taking non-const l-value references, because functions modifying their argument are never particularly readable. A method is expected to modify it's invocant (unless it's a getter), but modifying other arguments makes the data flow more complicated and less obvious. In C++11 move makes returning objects almost as efficient as modifying reference and std::tuple with std::tie allow returning multiple values, so most uses of argument modifying can be rewritten without significant loss of efficiency.

As for proper use of std::move note that most efficient way to write setters that were previously written taking const reference and copied from it should now take by value and move from it. The argument construction in caller will do move if possible and copy otherwise.