I was watching the video where Nicolai says that auto loses move semantics for this example:
template<typename Callable, typename... Args>
auto call(Callable&& op, Args&&... args) {
return std::invoke(std::forward<Callable>(op), std::forward<Args>(args)...);
}
I was wondering:
why is this the case?
does guaranteed RVO kick in in this example? If so what is the point of worrying about move?
The return value is a rvalue reference to the object. Working of Move Semantics in C++ Whenever there is a need to move the contents of the objects between the objects instead of copying the contents from one object to another object, we make use of Move Semantics in C++.
Then a move constructor is defined to initialize the move operation. Then the assignment of move operation is performed. Then getvarLength function is written which returns the length of the object. Then swap function to swap the content of the objects by making use of move semantics. Then getvarLength function returns the value of the object.
See my first answer for an example. Note that if an object does not manage at least one external resource (either directly, or indirectly through its member objects), move semantics will not offer any advantages over copy semantics. In that case, copying an object and moving an object means the exact same thing:
For that reason, C++11 has a special rule that allows returning automatic objects from functions without having to write std::move. In fact, you should never use std::move to move automatic objects out of functions, as this inhibits the "named return value optimization" (NRVO). Never use std::move to move automatic objects out of functions.
I think Nicolai could have just phrased it a bit better.
When you return by auto
, your function returns a value (the type of which will be deduced). If std::invoke
returns a pure rvalue or an xvalue, then of course the result of call
will be constructed accordingly (by a move, if possible). We don't "lose move semantics" in that sense.
But when we return by value, that value object needs to be created. It can be created by a move, and under certain circumstances (which aren't present here) it can be elided, but it must be created. Guaranteed copy elision doesn't allow us to cop out of creating this object.
That can be quite wasteful if std::invoke
gives us back an xvalue (an rvalue reference to something). Why should we construct an object to return it?
That's why a slide or two later he says we should return by decltype(auto)
. The deduction rules will then preserve the value cateogry of the the call to std::invoke
:
If it returns a value, we are no worse off. Our own call
will return a value (which it may create by moving the return value of std::invoke
).
If std::invoke
returns an lvalue(X&
) or an xvalue(X&&
), that will be returned as is, without creating another object by a copy or move.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With