0

The answer here says, for any object with one owning pointer.

Move constructor: 1 load and 2 stores.

Move assignment: 2 loads and 2 stores.

My question is, how does this work. My thoughts

Move assignment

MyClass& operator=(MyClass&& rhs);//lhs is this

First both lhs and rhs have to be loaded. Then rhs in stored to lhs. That accounts for two loads and one store. Is the rhs still stored back, even though nothing was changed and it is an rvalue?

Move constructor

MyClass(MyClass&& rhs);

This time only rhs in loaded into the register. Then it is stored to lhs. That is one load and one store. What than? Again, I am missing one store, as above.

River
  • 8,585
  • 14
  • 54
  • 67
  • With both operations the LHS has be cleanup (resouced released) before it can be assigned to; this can be cheaply done by swapping the internals of the LHS with the RHS (the RHS is temporary it's going to be destroyed anyway) . – Richard Critten Jan 20 '18 at 21:17

1 Answers1

2

As pointed in the linked answer for an optimized case of a class with a single owning-pointer:

Move constructor

MyClass(MyClass&& rhs):
    ptr(rhs.ptr) {            // load rhs.ptr, store it in ptr
    rhs.ptr = nullptr;        // store nullptr in rhs.ptr
}
  1. Load the rhs pointer value,
  2. store it in the new object and
  3. store nullptr in the rhs object

The move constructor do not need to load the current pointer because it is usually nullptr.

Move assignment

MyClass& operator=(MyClass&& rhs) {
    auto r3 = this->ptr;       // load this->ptr
    this->ptr = rhs.ptr;       // load rhs.ptr, store it in ptr
    rhs.ptr = r3;              // store rhs.ptr
    return *this;
}
  1. Load the current pointer value,
  2. load the rhs pointer value,
  3. store the rhs in the current object and
  4. store the old value from step 1. in the rhs, move-from object, thus transferring the ownership and making sure it will delete it.

So move assignment looks just like swap. No self-assignment check to prevent branching.

The assignment have to make sure that the memory pointed by the previous value of the pointer is freed and the source object is in valid state. The easiest way to ensure this is to store the old pointer in the rhs object and it will delete it.

Here you can see a slightly optimized code generated by clang 5.0.0 (https://godbolt.org/g/WxGxZ4):

MyClass::MyClass(MyClass&&): # @MyClass::MyClass(MyClass&&)
  mov rax, qword ptr [rsi]
  mov qword ptr [rdi], rax
  mov qword ptr [rsi], 0
  ret
MyClass::operator=(MyClass&&): # @MyClass::operator=(MyClass&&)
  mov rax, qword ptr [rdi]
  mov rcx, qword ptr [rsi]
  mov qword ptr [rdi], rcx
  mov qword ptr [rsi], rax
  mov rax, rdi
  ret
Mihayl
  • 3,821
  • 2
  • 13
  • 32