Generally speaking, move constructors and move assignment operators are only needed in two scenarios:
- You want your non-POD class to be efficiently movable (e.g. std::vector, std::list, etc);
- Your class cannot be copied for some reasons but it needs to be movable (e.g. std::unique_ptr, std::move_only_function, etc).
Both the copy and move constructors are particularly useful when a class has a member that is a raw pointer, which may point to memory allocated on the heap
Not just a memory resource but generally any type of resource/data that doesn t rest in the object representation of a class instance but still belongs to it. It might be a socket, thread, file handle, or something else. An example is the std::string
class which may own a buffer of characters that might be located somewhere else in the memory. In other words, the underlying buffer might not be inside a std::string
object itself. But it owns the buffer by having a pointer member to it plus a few other members.
Take a look at below example:
#include <string>
#include <utility>
#include <iostream>
class Foo
{
public:
Foo( const std::string& str, const double val )
: str_ { str }, val_ { val }
{}
// copy ctor
Foo( const Foo& rhs )
: str_ { rhs.str_ }, val_ { rhs.val_ }
{}
// move ctor
Foo( Foo&& rhs ) noexcept
: str_ { std::move( rhs.str_ ) }, val_ { std::exchange( rhs.val_, 0.0 ) }
{}
void print_members()
{
std::cout << str_ << " " << val_ << "
";
}
private:
std::string str_;
double val_;
};
int main( )
{
Foo f { "some text", 1.0 };
Foo f1 { f };
Foo f2 { std::move( f ) };
f.print_members( ); // " 0" semantically empty after being *moved from*
f1.print_members( ); // "some text 1"
f2.print_members( ); // "some text 1"
}
As can be seen, the creation of f2
through move construction will be more efficient than the creation of f1
through copy construction. Because it simply moves the f.str_
to f2.str_
so it does not copy the underlying buffer of f.str_
to that of f2.str_
. In other words, it simply moves the ownership of that buffer of char
s to f2
. Also, note that the above class doesn t need its copy/move constructors explicitly defined since the compiler can create them implicitly and more efficiently. However, they were implemented for the purpose of demonstration.
what if my class doesn`t have a pointer member just a collection of members of integers and strings and array of doubles
Yes, obviously implementing a move constructor/assignment operator for a POD class (more accurately standard layout) is not going to increase efficiency in most cases since those operations will be equivalent to copying that class. So for instance, if the above Foo
class didn t have a std::string
member it would be a standard layout type and would not necessarily need move semantics implemented.
Note: The following is not always true
the copy constructor takes a non-temporary object as input, while the move constructor takes a temporary object
The copy constructor can take temporaries (rvalues) too. And the move constructor can take lvalues that were cast to xvalue via std::move
as shown in Foo f2 { std::move( f ) };
where f
is a non-temporary.