Move Semantics
Definination of std::move
template<class T>
constexpr remove_reference_t<T>&& move(T&& t) noexcept {
return static_cast<remove_reference_t<T>&&>(t)
}
std::move
doesn’t move anything. It converts any expression into an rvalue so it can be bound to an rvalue reference.
RValue References Guideline
- No rvalue reference to const type
- Use a non-const rvalue reference instead.
- Most uses of rvalue references modify the object being referenced
- Most rvalues are not const
- No rvalue reference as function return type
- Core Guideline F.45 : Don’t return a
T&&
- Return by value instead.
- see C++中的右值引用与std::move
- Core Guideline F.45 : Don’t return a
- Rvalue references often bind to temporaries, which don’t outlive the function
std::move
Guidelines
- Next operation after std::move is destruction or assignment
- The object that was moved from shouldn’t be used again
- Don’t
std::move
the return of a local variable- Core Guideline F.48: Don’t
return std::move(local)
- C++ Standard has a special rule for this
- The return expression is an rvalue if it is a local variable of parameter
std::string func(std::string param, std::string* ptr){ std::string local = "Hello"; *ptr = local; if(some_condition()){ return local; //rvalue, calls the move constructor } return *ptr; //lvalue, calls the copy constructor }
- Core Guideline F.48: Don’t
Move Constructor
- Implicitly declared move constructor if there no user-defined
- destructor
- copy constructor
- copy assignment operator
- move assignment operator
- Implicitly declared or explicitly use
=default
to let compiler generate move constructor for you- Move constructs each base and non-static data member
- Deleted if any base or non-static data member cannot be move constructed
struct s {
double* data;
s( s&& other) noexcept
: data(std::move(other.data)){
other.data = nullptr;
}
//a more elegant way is using std::exchange
s (s && other) noexcept
: data(std::exchange(other.data, nullptr))
{}
};
Move Assignment Operator
- Implicitly declared move constructor if there no user-defined
- destructor
- copy constructor
- copy assignment operator
- move constructor
- Implicitly declared or explicitly use
=default
to let compiler generate move assignment operator for you- Move assigns each base and non-static data member
- Deleted if any base or non-static data member cannot be move assigned
struct s {
double* data;
S& operator=(S&& other) noexcept{
if(this == &other) return *this;
delete[] data;
data = std::exchange(other.data, nullptr)
return *this;
}
};
Move Copy/Assignment Guidelines:
- Move constructor/assignment should be explicitly noexcept
- Core Guideline C.66: Make move operators no except
- Moves are supposed to transfer resources, not allocate or acquire resources. No exceptions should be thrown.
- Declare it
noexcept
even when it is defined as defaultfoo(foo&&) noexcept = default
- Move-from object must be left in a valid state
- Core Guideline C.64: A move operation should move and leave its source in a valid state
- Prefer to leave it in the default constructed state
- But that is not always practical
struct s{ std::string str; std::size_t len; //Invariant: len == str.length() s(s&& other) noexcept : str(std::move(other.str)), len(std::move(other.len)){ //reset other to a valid state other.str.clear(); other.len = 0; } }
Summary
- Use
=default
when possible- Core Guideline C.80: Use
=default
if you have to be explicit about using the default semantics
- Core Guideline C.80: Use
- Rule of 5 / Rule of 0
- Core Guideline C.21: If you define or
=delete
any copy, move, or destructor function, define or=delete
them all- destructor
- copy constructor
- copy assignment operator
- move constructor
- move assignment operator
- Rule of 0: If default behavior is correct for all five, let compiler do everything
- Rule of 1: If you must define one of the five, declare all of them explicitly
- Core Guideline C.21: If you define or