Reputation: 174
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:
Upvotes: 2
Views: 325
Reputation: 174
Well, I would like to make a summary of this question.
if the move constructor is trivial (generated by the compiler), then std::move() only takes a bitwise copy. Which means vec.emplace_back(std::move(b)) is equivalent to vec.emplace_back(b).
following is another better example to show the difference. We try to construct both members _x and _y with move semantic. Since X has a user-declared move ctor but Y hasn't, X's move ctor gets called, and Y's copy ctor gets called.
class X {
public:
X() { cout << "X()" << endl; }
X(X const&) { cout << "X(X const&)" << endl; }
X(X&&) noexcept { cout << "X(X&&)" << endl; }
};
class Y {
public:
Y() { cout << "Y()" << endl; }
Y(Y const&) { cout << "Y(Y const&)" << endl; }
};
struct Box {
Box(X&& x, Y&& y) : _x(std::move(x)), _y(std::move(y)) {}
X _x;
Y _y;
};
int main() {
X x;
Y y;
Box bb(std::move(x), std::move(y));
return 0;
}
/*
output:
X()
Y()
X(X&&)
Y(Y const&)
*/
Upvotes: 0
Reputation: 238311
- std::move() is completely redundant for this case.
Technically not a question but yes, this is correct.
- Moreover, if the parameter of std::move() has a trivial move constructor, then std::move() is redundant.
Same as above.
- if the trivial move constructor performs the same action as the trivial copy constructor, why the compiler generates different disassembly?
Possibly because you call the function std::move
in one but not the other.
It doesn't have to produce different assembly however since the observable behaviour is identical. My compiler produces identical assembly.
Upvotes: 6