The difference between f(A&&) and f(A)

Let us consider the following code:


#include
#include

class A {
  int a;
public:
  A(A&& a) {}
  A(int _a) : a(_a){}
  int f(A a) {
    std::cout << a.a << std::endl;
  }
};

int main()
{
  A a(1);
  a.f(A(2));
  return 0;
}

It generates the following code:
std::piecewise_construct:
.zero 1
A::A(int):
pushq %rbp
movq %rsp, %rbp
movq %rdi, -8(%rbp)
movl %esi, -12(%rbp)
movq -8(%rbp), %rax
movl -12(%rbp), %edx
movl %edx, (%rax)
nop
popq %rbp
ret
A::f(A):
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -16(%rbp), %rax
movl (%rax), %eax
movl %eax, %esi
movl std::cout, %edi
call std::basic_ostream >::operator<<(int)
movl std::basic_ostream >& std::endl >(std::basic_ostream >&), %esi
movq %rax, %rdi
call std::basic_ostream >::operator<<(std::basic_ostream >& (*)(std::basic_ostream >&))
nop
leave
ret
main:
pushq %rbp
movq %rsp, %rbp
subq $32, %rsp
leaq -32(%rbp), %rax
movl $1, %esi
movq %rax, %rdi
call A::A(int)
leaq -16(%rbp), %rax
movl $2, %esi
movq %rax, %rdi
call A::A(int)
leaq -16(%rbp), %rdx
leaq -32(%rbp), %rax
movq %rdx, %rsi
movq %rax, %rdi
call A::f(A)
movl $0, %eax
leave
ret
__static_initialization_and_destruction_0(int, int):
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
cmpl $1, -4(%rbp)
jne .L7
cmpl $65535, -8(%rbp)
jne .L7
movl std::__ioinit, %edi
call std::ios_base::Init::Init()
movl $__dso_handle, %edx
movl std::__ioinit, %esi
movl std::ios_base::Init::~Init(), %edi
call __cxa_atexit
.L7:
nop
leave
ret
pushq %rbp
movq %rsp, %rbp
movl $65535, %esi
movl $1, %edi
call __static_initialization_and_destruction_0(int, int)
popq %rbp
ret

Let's slightly change the code:

#include
#include

class A {
  int a;
public:
  A(A&& a) {}
  A(int _a) : a(_a){}
  int f(A&& a) {
    std::cout << a.a << std::endl;
  }
};

int main()
{
  A a(1);
  a.f(std::move(A(2)));
  return 0;
}

It then generates the following snippet:

std::piecewise_construct:
.zero 1
A::A(int):
pushq %rbp
movq %rsp, %rbp
movq %rdi, -8(%rbp)
movl %esi, -12(%rbp)
movq -8(%rbp), %rax
movl -12(%rbp), %edx
movl %edx, (%rax)
nop
popq %rbp
ret
A::f(A&&):
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -16(%rbp), %rax
movl (%rax), %eax
movl %eax, %esi
movl std::cout, %edi
call std::basic_ostream >::operator<<(int)
movl std::basic_ostream >& std::endl >(std::basic_ostream >&), %esi
movq %rax, %rdi
call std::basic_ostream >::operator<<(std::basic_ostream >& (*)(std::basic_ostream >&))
nop
leave
ret
std::remove_reference::type&& std::move(A&&):
pushq %rbp
movq %rsp, %rbp
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
popq %rbp
ret
main:
pushq %rbp
movq %rsp, %rbp
subq $32, %rsp
leaq -32(%rbp), %rax
movl $1, %esi
movq %rax, %rdi
call A::A(int)
leaq -16(%rbp), %rax
movl $2, %esi
movq %rax, %rdi
call A::A(int)
leaq -16(%rbp), %rax
movq %rax, %rdi
call std::remove_reference::type&& std::move(A&&)
movq %rax, %rdx
leaq -32(%rbp), %rax
movq %rdx, %rsi
movq %rax, %rdi
call A::f(A&&)
movl $0, %eax
leave
ret
__static_initialization_and_destruction_0(int, int):
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
cmpl $1, -4(%rbp)
jne .L9
cmpl $65535, -8(%rbp)
jne .L9
movl std::__ioinit, %edi
call std::ios_base::Init::Init()
movl $__dso_handle, %edx
movl std::__ioinit, %esi
movl std::ios_base::Init::~Init(), %edi
call __cxa_atexit
.L9:
nop
leave
ret
pushq %rbp
movq %rsp, %rbp
movl $65535, %esi
movl $1, %edi
call __static_initialization_and_destruction_0(int, int)
popq %rbp
ret

So here we see the difference, the second case has more code:

std::remove_reference::type&& std::move(A&&):
pushq %rbp
movq %rsp, %rbp
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
popq %rbp
ret

...

movq %rax, %rdi
call std::remove_reference::type&& std::move(A&&)
movq %rax, %rdx

Comments

Popular Posts