Please take a look at the following code snippet, which seems std::move() is low efficient in this scenario.
class A {};
struct B {
double pi{ 3.14 };
int i{ 100 };
A* pa{ nullptr };
};
int main() {
B b;
std::vector<B> vec;
vec.emplace_back(b); // 1) without move
vec.emplace_back(std::move(b)); // 2) with move
return 0;
}
I got the following disassembly in visual studio 2019 [C++ 14, Release]:
vec.emplace_back(b); // 1) without move
00E511D1 push eax
00E511D2 push 0
00E511D4 lea ecx,[vec]
00E511D7 call std::vector<B,std::allocator<B> >::_Emplace_reallocate<B> (0E512C0h)
vec.emplace_back(std::move(b)); // 2) with move
00E511DC mov eax,dword ptr [ebp-18h]
00E511DF cmp eax,dword ptr [ebp-14h]
00E511E2 je main+91h (0E511F1h)
00E511E4 movups xmm0,xmmword ptr [b]
00E511E8 movups xmmword ptr [eax],xmm0
00E511EB add dword ptr [ebp-18h],10h
00E511EF jmp main+9Eh (0E511FEh)
00E511F1 lea ecx,[b]
00E511F4 push ecx
00E511F5 push eax
00E511F6 lea ecx,[vec]
00E511F9 call std::vector<B,std::allocator<B> >::_Emplace_reallocate<B> (0E512C0h)
It's easy to see that the move version takes more unnecessary work. According to the description here, the compiler will generate a trivial move constructor for struct B and this trivial move constructor will take a copy semantic.
Then my questions are:
- std::move() is completely redundant for this case.
- Moreover, if the parameter of std::move() has a trivial move constructor, then std::move() is redundant.
- if the trivial move constructor performs the same action as the trivial copy constructor, why the compiler generates different disassembly? Actually, this is the most confusing for me.