Consider the following definition of Foo:
struct Foo {
uint64_t data;
};
Now, consider the following definition of Bar, which has the same data member as Foo, but has an empty user-declared destructor:
struct Bar {
~Bar(){} // <-- empty user-declared dtor
uint64_t data;
};
Using gcc 8.2 with -O2, the function copy_foo():
void copy_foo(const Foo* src, Foo* dst, size_t len) {
std::copy(src, src + len, dst);
}
results in the following assembly code:
copy_foo(Foo const*, Foo*, size_t):
salq $3, %rdx
movq %rsi, %rax
je .L1
movq %rdi, %rsi
movq %rax, %rdi
jmp memmove
.L1:
ret
The assembly code above calls memmove() in order to perform the copy of the contiguous Foo objects. However, the function below, copy_bar(), which does exactly the same as copy_foo(), but for Bar objects:
void copy_bar(const Bar* src, Bar* dst, size_t len) {
std::copy(src, src + len, dst);
}
generates the following assembly code:
copy_bar(Bar const*, Bar*, size_t):
salq $3, %rdx
movq %rdx, %rcx
sarq $3, %rcx
testq %rdx, %rdx
jle .L4
xorl %eax, %eax
.L6:
movq (%rdi,%rax,8), %rdx
movq %rdx, (%rsi,%rax,8)
addq $1, %rax
movq %rcx, %rdx
subq %rax, %rdx
testq %rdx, %rdx
jg .L6
.L4:
ret
This assembly code doesn't call memmove(), but performs the copy by itself.
Of course, if Bar is instead defined as:
struct Bar {
~Bar() = default; // defaulted dtor
uint64_t data;
};
Then, both functions result in identical assembly code, since Foo also has a defaulted destructor.
Is there any reason why user-declaring an empty destructor in a class prevents the compiler from generating a call to memmove() to copy contiguous objects of that class?