Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can compiler generate std::move for a last use of lvalue automatically?

A code like this is often seen in r-value references articles:

Dave Abrams: Move It With Rvalue References

void g(X);

void f()
{
    X b;
    g(b);              // still need the value of b
    …
    g( std::move(b) ); // all done with b now; grant permission to move
}

Could a compiler generate this optimization automatically, i.e. to detect a l-value is going to be destructed anyway and could be moved from, or would this be a violation of the standard, assuming a generic case the compiler does not know anything about how is move, copy or destruct implemented for the X class?

If such optimization is allowed, is it performed by some compiler in practice?

like image 733
Suma Avatar asked Mar 13 '13 13:03

Suma


People also ask

Does C++ automatically move?

That's pretty much the only time you should write std::move , because C++ already uses move automatically when copying from an object it knows will never be used again, such as a temporary object or a local variable being returned or thrown from a function.

Does std :: move do anything?

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.

Can you std :: move a reference?

"std::move" should only be used where moving can happen When passing the result of std::move as a const reference argument. In this case, no object will be moved since it's impossible to call the move constructor from within the function.

When should we use std :: move?

In C++11, std::move is a standard library function that casts (using static_cast) its argument into an r-value reference, so that move semantics can be invoked. Thus, we can use std::move to cast an l-value into a type that will prefer being moved over being copied.


3 Answers

No. Consider:

using X = std::shared_ptr<int>;
void g(X);
void f() {
    X b = std::make_shared<int>();
    int &i = *b;
    g(b);              // last use of 'b'
    i = 5;
}

In general, the compiler cannot assume that altering the semantics of copies, moves and destructors of X will be a legitimate change without performing analysis on all the code surrounding the use of b (i.e., the whole of f, g, and all the types used therein).

Indeed, in some cases whole-program analysis may be necessary:

using X = std::shared_ptr<std::lock_guard<std::mutex>>;
std::mutex i_mutex;
int i;
void g(X);
void f() {
    X b = std::make_shared<std::lock_guard<std::mutex>>(i_mutex);
    g(b);              // last use of 'b'
    i = 5;
}

If b is moved, this introduces a data race against other threads that synchronize access to i using i_mutex.

like image 75
ecatmur Avatar answered Oct 04 '22 09:10

ecatmur


Could a compiler do it? Only as an explicit language extension, because the standard doesn't allow them to make such an optimization without that.

Should they do it? No. The meaning of g(b) should be based on the definition of g and b. g should be some callable type, which has an overload that takes something that b can be implicitly converted into. Given access to the definition of all available gs, and the definition of b, you should be able to determine exactly what function will be called.

To allow this "optimization" now means that this is impossible. g(b) might perform a move, and it might not, depending on exactly where g(b) happens to be in a function. That's not a good thing.

return is allowed to get away with it, but only because it still has the same meaning. return b will always attempt to move from b if b is a value-type who's lifetime is restricted to the scope of the function.

like image 28
Nicol Bolas Avatar answered Oct 04 '22 11:10

Nicol Bolas


(...) assuming a generic case the compiler does not know anything about how is move, copy or destruct implemented for the X class?

No, compilers are not allowed to do optimisations based on faith.

For clarity, this question is unrelated to copy elision: compilers may be allowed to elide a copy, but they cannot change copies to moves willy-nilly.

like image 26
R. Martinho Fernandes Avatar answered Oct 04 '22 11:10

R. Martinho Fernandes