Move Semantics
std::move
-
std::move
unconditionally casts its input to rvalue reference. -
std::move
does not move anything
template <typename T>
std::remove_reference_t<T>&& move(T&& t) noexcept
{
return static_cast<std::remove_reference_t<T>&&>( t );
}
std::move
is semantic transfer of ownership.
lvalue and rvalue
lvalue: Some value that has a name
rvalue: Value with no name.
std::string s;
s + s = s
The above code is perfectly valid. s+s
is a rvalue and s
is a lvalue inspite of beign on the opposite side of the =
operator.
Move Constructor
In the move constructor all the members are moved explicitly using their move constructor. If we do not do so, the move constructor will only copy the variables.
For the pointers, when we perform the move operation, we need to set the source pointer to nullptr
. We use std::exchange
.
std::exchange
template< class T, class U = T >
T exchange( T& obj, U&& new_value );
constexpr T exchange( T& obj, U&& new_value )
- Replaces the value of obj with new_value and returns the old value of obj.
Example of Move constructor
class Widget {
private:
int i{0};
std::string s{};
int *p{ nullptr };
` public:
// Move Constructor
Widget(Widget&& w) noexcept
:i (std::move(w.i))
,s (std::move(w.s))
,p (std::exchange(w.pi, nullptr))
{
}
}
- The constructor is
noexcept
. It means if the constructor fails, it will behave as no operation was performed. Thenoexcept
is for the speed up of the constructor. The speed up due to this is 60%. - C++ Core guidelin: Ideally that is moved-from should be set to the default value type. If its not set there must be a very strong reason to do so.
In the above implementation wheni
is moved its value after moving should be defaulted to0
.
If the move constructor is defaulted then the moved-from values have undefined state.
// No need to rest the unique pointer
std::unique_ptr<int> p{};
Widget(Widget&& w) noexcept
:i (std::move(w.i))
,s (std::move(w.s))
,p (std::move(w.p));
{ }
// The above constructor can be defaulted.
// Default is also no except.
Widget(Widget&& w) = default;
Move assignment operator
- Clean up all visible resources
- Transfer the contents of
w
intothis
. - Leave
w
in a valid but undefined state.
Widget operator=(Widget&& w)
{
// free the current resource
delete pi;
i = std::move(w.i);
s = std::move(s.i);
// below can be replaced by std::exchange
pi = std::move(w.pi)=;
w.pi = nullptr;
return *this;
}
- The current pointer may be holding some resource. If we perform move operation, then the resource is lost and never evicted from the memory. Therefore delete the pointer and then perform the move operation.
// Takes rvalues
vector& operator=(vector&& rhs )
std::vector v1{};
std::vector v2{1,2,3};
/*
* std::move() converts lvalue to Xvalue
* Xvalue means rvalue that expires.
*/
v1 = std::move(v2);
Here the v2
becomes invalid when its moved to v1
, but its still alive. The copy assignment can again make v2
valid.